# Custom Instrumentation | Sentry for Symfony

To capture transactions and spans customized to your organization's needs, you must first [set up tracing.](https://docs.sentry.io/platforms/php/guides/symfony/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, `expensive_operation`), and sends the result to Sentry:

```php
// Setup context for the full transaction
$transactionContext = \Sentry\Tracing\TransactionContext::make()
    ->setName('Example Transaction')
    ->setOp('http.server');

// Start the transaction
$transaction = \Sentry\startTransaction($transactionContext);

// Set the current transaction as the current span so we can retrieve it later
\Sentry\SentrySdk::getCurrentHub()->setSpan($transaction);

// Setup the context for the expensive operation span
$spanContext = \Sentry\Tracing\SpanContext::make()
    ->setOp('expensive_operation');

// Start the span
$span1 = $transaction->startChild($spanContext);

// Set the current span to the span we just started
\Sentry\SentrySdk::getCurrentHub()->setSpan($span1);

// Calling expensive_operation()
expensive_operation();

// Finish the span
$span1->finish();

// Set the current span back to the transaction since we just finished the previous span
\Sentry\SentrySdk::getCurrentHub()->setSpan($transaction);

// Finish the transaction, this submits the transaction and it's span to Sentry
$transaction->finish();
```

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

The next example contains the implementation of the hypothetical `expensive_operation` function called from the code snippet in the previous section. But this time we are using the `trace` function to create a new span and set it as the current span which removes a lot of boilerplate.

You can choose the values of `op` and `description`.

```php
function expensive_operation(): void
{
    $spanContext = \Sentry\Tracing\SpanContext::make()
        ->setOp('some_operation')
        ->setDescription('This is a description');

    \Sentry\trace(function () {
        // Do the expensive operation...
    }, $spanContext);
}
```

You can also do this manually if you want to have more control over the span creation:

```php
function expensive_operation(): void
{
    $parent = \Sentry\SentrySdk::getCurrentHub()->getSpan();
    $span = null;

    // Check if we have a parent span (this is the case if we started a transaction earlier)
    if ($parent !== null) {
        $context = \Sentry\Tracing\SpanContext::make()
            ->setOp('some_operation')
            ->setDescription('This is a description');
        $span = $parent->startChild($context);

        // Set the current span to the span we just started
        \Sentry\SentrySdk::getCurrentHub()->setSpan($span);
    }

    try {
        // Do the expensive operation...
    } finally {
        // We only have a span if we started a span earlier
        if ($span !== null) {
            $span->finish();

            // Restore the current span back to the parent span
            \Sentry\SentrySdk::getCurrentHub()->setSpan($parent);
        }
    }
}
```

This code checks for the existence of a parent span and if there is one, a new span is created and set as the current span. Then we call the expensive operation. Finally, we finish the span and restore the current span to the parent span.

## [Setting and Retrieving the Current Transaction](https://docs.sentry.io/platforms/php/guides/symfony/tracing/instrumentation/custom-instrumentation.md#setting-and-retrieving-the-current-transaction)

In cases where you want to attach spans to an already ongoing transaction, you can use `getTransaction()` on the current hub. This property will return a `\Sentry\Tracing\Transaction` in cases where there is a transaction set; otherwise, it returns `null`.

```php
$transaction = \Sentry\SentrySdk::getCurrentHub()->getTransaction();

if ($transaction !== null) {
    $context = \Sentry\Tracing\SpanContext::make();
        ->setOp('some_operation');
    $span = $transaction->startChild($context);

    // ...

    $span->finish();
}
```

You can also set the current transaction after you started one:

```php
$transactionContext = \Sentry\Tracing\TransactionContext::make()
    ->setName('Example Transaction');
    ->setOp('http.server');

$transaction = \Sentry\startTransaction($transactionContext);

// A transaction is a span so we set it using `setSpan`
\Sentry\SentrySdk::getCurrentHub()->setSpan($transaction);
```

## [Setting and Retrieving the Current Span](https://docs.sentry.io/platforms/php/guides/symfony/tracing/instrumentation/custom-instrumentation.md#setting-and-retrieving-the-current-span)

In cases where you want to attach spans to an already ongoing span, you can use `getSpan()` on the current hub. This property will return a `\Sentry\Tracing\Span` in cases where there is a span set; otherwise, it returns `null`. You can also set the current span after you started one and restore it back to the parent after your span ends.

```php
$parentSpan = \Sentry\SentrySdk::getCurrentHub()->getSpan();

if ($parentSpan !== null) {
    $context = \Sentry\Tracing\SpanContext::make()
        ->setOp('some_operation');
    $span = $parentSpan->startChild($context);

    // Set the span we just started as the current span
    \Sentry\SentrySdk::getCurrentHub()->setSpan($span);

    // ...

    $span->finish();

    // Restore the span back to the parent span
    \Sentry\SentrySdk::getCurrentHub()->setSpan($parentSpan);
}
```

## [Adding Span & Transaction Data Attributes](https://docs.sentry.io/platforms/php/guides/symfony/tracing/instrumentation/custom-instrumentation.md#adding-span--transaction-data-attributes)

You can capture data attributes along with your spans and transactions. You can specify data attributes when starting a span or transaction:

```php
// Create a transaction and assign data attributes...
$transactionContext = \Sentry\Tracing\TransactionContext::make()
    ->setName('Example Transaction')
    ->setOp('http.server')
    ->setData([
        'data_attribute_1' => 42,
        'data_attribute_2' => true,
    ]);
$transaction = \Sentry\startTransaction($transactionContext);

// ... or create a span and assign data attributes

$spanContext = \Sentry\Tracing\SpanContext::make()
    ->setOp('http.client')
    ->setData([
        'data_attribute_1' => 42,
        'data_attribute_2' => true,
    ]);
$transaction->startChild($context);
```

Or you can add data attributes to an existing span or transaction:

```php
$transaction = \Sentry\SentrySdk::getCurrentHub()->getTransaction();
if ($transaction !== null) {
    $transaction->setData([
        'data_attribute_1' => 42,
        'data_attribute_2' => true,
    ]);
}

$span = \Sentry\SentrySdk::getCurrentHub()->getSpan();
if ($span !== null) {
    $span->setData([
        'data_attribute_1' => 42,
        'data_attribute_2' => true,
    ]);
}
```

To update an existing data attribute, you can combine `setData()` with `getData()`:

```php
$span->setData([
    'data_attribute_1' => $span->getData('data_attribute_1', 0) + 1,
]);

$transaction->setData([
    'data_attribute_1' => $transaction->getData('data_attribute_1', 0) + 1,
]);
```

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

```php
\Sentry\init([
    'dsn' => '___PUBLIC_DSN___',
    'before_send_transaction' => function(Event $event) {
        $spans = $event->getSpans();
        foreach ($spans as $span) {
            $span->setData([
                'data_attribute_1' => 42,
                'data_attribute_2' => true,
            ]);
        }
    
        $event->contexts['trace']['data']['foo'] = 42;
    },
]);
```
