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

# Custom Instrumentation | Sentry for Rails

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

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

The following example creates a transaction for a scope that contains an expensive operation (for example, `process_item`), and sends the result to Sentry:

```ruby
# start a transaction
transaction = Sentry.start_transaction(op: "process_item")

# set the transaction on the scope so children span are attached to this transaction
Sentry.get_current_scope.set_span(transaction)

# perform the operation
process_item(args)


# finish the transaction, which will send it to Sentry automatically
transaction.finish
```

## [Add More Spans to the Transaction](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation.md#add-more-spans-to-the-transaction)

The next example contains the implementation of the hypothetical `process_item` function called from the code snippet in the previous section. Our SDK can determine if there is currently an open transaction and add all newly created spans as child operations to that transaction. You can choose the values of `op` and `description`.

```ruby
class OrdersController < ApplicationController
  def create
    order = Order.new

    Sentry.with_child_span(op: :process_items, description: 'processing items') do |span|
      span&.set_data(:key, 'value')

      order.process_items(params)
    end
  end # the child span ends with the block
end
```

Your new span will be nested under whichever span is currently running on the scope, otherwise it will be at the root of the transaction event.

Alternatively, you can manually grab the current transaction and use its `with_child_span` method to always create a top-level span.

```ruby
class OrdersController < ApplicationController
  def create
    order = Order.new
    transaction = Sentry.get_current_scope.get_transaction

    if transaction
      transaction.with_child_span(op: :process_items, description: 'processing items') do |span|
        span&.set_data(:key, 'value')
        order.process_items(params)
      end # the child span ends with the block
    else
      order.process_items(params)
    end
  end
end
```

## [Retrieve a Transaction](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation.md#retrieve-a-transaction)

In cases where you want to attach Spans to an already ongoing Transaction you can use `Sentry.get_current_scope.get_transaction`. This property will return a `Transaction` in case there is a running Transaction otherwise it returns `nil`.

```ruby
transaction = Sentry.get_current_scope.get_transaction || Sentry.start_transaction(name: 'task')

span = transaction.start_child(op: 'operation')
# perform the operation
span.finish if span
```

## [Retrieve the Current Span](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation.md#retrieve-the-current-span)

Started spans are stored in the scope, and can be fetched off the scope:

```ruby
span = Sentry.get_current_scope.get_span
```

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

### [Adding Data Attributes to Transactions](https://docs.sentry.io/platforms/ruby/guides/rails/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 strings, numbers or booleans, as well as (non-mixed) arrays of these types:

```ruby
Sentry.start_transaction(name: "my-transaction") do |transaction|
  transaction.set_data("my-data-attribute-1", "value1")
  transaction.set_data("my-data-attribute-2", 42)
  transaction.set_data("my-data-attribute-3", true)

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

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

You can add data attributes to your spans the same way, with the same type restrictions as described above.

```ruby
Sentry.with_child_span(op: "my-span") do |span|
  next unless span

  span.set_data("my-data-attribute-1", "value1")
  span.set_data("my-data-attribute-2", 42)
  span.set_data("my-data-attribute-3", true)

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

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

To attach data attributes to the transaction and all its spans, you can use [`before_send_transaction`](https://docs.sentry.io/platforms/ruby/guides/rails/configuration/filtering.md#using-before-send-transaction):

```ruby
Sentry.init do |config|
  # ...
  config.traces_sample_rate = 1.0

  config.before_send_transaction = lambda do |event, _hint|
    # Set the data attribute "foo" to "bar" on every span belonging to this transaction event
    event.spans.each { |span| span.set_data(:foo, "bar")

    # Set the data on the transaction itself, too
    event.contexts[:trace][:data][:foo] = "bar"

    event
  end
end
```

## Pages in this section

- [Instrument AI Agents](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation/ai-agents-module.md)
- [Instrument Caches](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation/caches-module.md)
- [Instrument HTTP Requests](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation/requests-module.md)
- [Instrument Queues](https://docs.sentry.io/platforms/ruby/guides/rails/tracing/instrumentation/custom-instrumentation/queues-module.md)
