Custom Instrumentation

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

Copied
// Transaction can be started by providing the name and the operation
let tx_ctx = sentry::TransactionContext::new(
    "transaction name",
    "transaction operation",
);
let transaction = sentry::start_transaction(tx_ctx);

// Transactions can have child spans, and those spans can have child spans as well.
let span = transaction.start_child("span operation", "span description");

// ...
// 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:

Copied
// Let's say this method is called in a background thread when a user clicks on the checkout button of your shop
fn perform_checkout() {
    // This will create a new Transaction for you
    let tx_ctx = sentry::TransactionContext::new(
        "checkout",
        "perform-checkout",
    );
    let transaction = sentry::start_transaction(tx_ctx);

    // Validate the cart
    let validation_span = transaction.start_child(
        "validation",
        "validating shopping cart",
    );

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

    validation_span.finish();

    // Process the order
    let process_span = transaction.start_child(
        "process",
        "processing shopping cart",
    )

    process_shopping_cart() //Another time consuming process.

    process_span.finish();

    transaction.finish();
}

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 transaction.finish() will finish the transaction and send it to Sentry.

Retrieve a Transaction or Span

In cases where you want to attach spans to an already ongoing transaction, you can use Scope::get_span. This method will return a TransactionOrSpan in case there is a running transaction or span; otherwise it returns None.

Transactions or spans need to be manually attached to the scope using Scope::set_span.

Copied
// Retrieve the currently running span
let parent_span = sentry::configure_scope(|scope| scope.get_span());

let span: sentry::TransactionOrSpan = match &parent_span {
    Some(parent) => parent.start_child("subtask", "description").into(),
    None => {
        let ctx = sentry::TransactionContext::new("task", "op");
        sentry::start_transaction(ctx).into()
    }
};

// Set the currently running span
sentry::configure_scope(|scope| scope.set_span(Some(span)));

Transactions for Async Tasks

Special care must be taken when spawning operations as independent threads or asynchronous tasks.

In situations where the computation can run longer than the calling context, you should always start a new transaction. The SDK offers the TransactionContext::continue_from_span method to make that more convenient.

Copied
let parent_span = sentry::configure_scope(|scope| scope.get_span());
// Create a new transaction as an independent continuation
let ctx = sentry::TransactionContext::continue_from_span(
    "A long-running spawned task",
    "spawned_task",
    parent_span,
);

let task = async move {
  let transaction = sentry::start_transaction(ctx);
  // Set the newly created transaction as the *current span*
  sentry::configure_scope(|scope| scope.set_span(Some(transaction.clone().into())));

  // Do the actual computation
  // ...

  // Finish the span and send it to sentry
  transaction.finish();
  // The task gets a
}.bind_hub(Hub::new_from_top(Hub::current()));

task::spawn(task);

Connect Errors With Spans

Sentry errors can be linked with transactions and spans.

Errors reported to Sentry while a transaction or span is bound to the scope are linked automatically:

Copied
let tx_ctx = sentry::TransactionContext::new(
    "checkout",
    "perform-checkout",
);
let transaction = sentry::start_transaction(tx_ctx);

// Bind the transaction / span to the scope:
sentry::configure_scope(|scope| scope.set_span(Some(transaction.into())));

// The error is linked to the transaction / span:
let err = "NaN".parse::<usize>().unwrap_err();
sentry::capture_error(&err);

transaction.finish();

Distributed Tracing

Traces can bridge across multiple software services. Each span in a trace can be represented as a sentry-trace header, containing the trace id, span id, and sampling details. This sentry-trace header can be passed to downstream services so that they can create spans that are a continuation of the trace started in the originating service.

To obtain trace headers from a span, use the iter_headers() method. Then pass these to the downstream service. If the communication happens over HTTP, it is recommended to attach all headers to the outgoing HTTP request.

For example when using the reqwest crate:

Copied
let client = reqwest::Client::new();

let mut request = client.get("downstream-url");
if let Some(span) = sentry::configure_scope(|scope| scope.get_span()) {
    for (k, v) in span.iter_headers() {
        request = request.header(k, v);
    }
}

let response = request.send().await?;

To create a span as a continuation of the trace retrieved from the upstream service, pass an iterator of the incoming headers to the transaction context:

Copied
// This needs to be a `impl Iterator<Item = (&str, &str)>` of header key/value pairs.
let headers = request.headers();
let tx_ctx = sentry::TransactionContext::continue_from_headers(
    "transaction name",
    "http.server",
    headers,
);
let transaction = sentry::start_transaction(tx_ctx);
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) to suggesting an update ("yeah, this would be better").