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

# Custom Instrumentation | Sentry for Logback

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

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

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

```java
import io.sentry.ITransaction;
import io.sentry.Sentry;
import io.sentry.SpanStatus;

// A good name for the transaction is key, to help identify what this is about
ITransaction transaction = Sentry.startTransaction("processOrderBatch()", "task");
try {
  processOrderBatch();
} catch (Exception e) {
  transaction.setThrowable(e);
  transaction.setStatus(SpanStatus.INTERNAL_ERROR);
  throw e;
} finally {
  transaction.finish();
}
```

By default the transaction is not bound to the scope. Our auto instrumentation, for example those instrumenting database requests or HTTP calls, rely on a transaction to be present in order to attach new child spans to it. Please take a look at the [Create Transaction Bound to The Current Scope](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation.md#create-transaction-bound-to-the-current-scope) section further down.

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

By default, transactions are not bound to the scope. Transaction has to be passed manually as a method parameter to enable attaching nested spans. When creating nested span, you can choose the value of `operation` and `description`.

```java
import java.io.FileNotFoundException;

import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.Sentry;
import io.sentry.SpanStatus;

// A good name for the transaction is key, to help identify what this is about
ITransaction transaction = Sentry.startTransaction("processOrderBatch()", "task");
try {
  processOrderBatch(transaction);
} catch (Exception e) {
  transaction.setThrowable(e);
  transaction.setStatus(SpanStatus.INTERNAL_ERROR);
  throw e;
} finally {
  transaction.finish();
}

void processOrderBatch(ISpan parentSpan) {
  // span operation: task, span description: operation
  ISpan innerSpan = parentSpan.startChild("task", "operation");
  try {
    // omitted code
  } catch (FileNotFoundException e) {
    innerSpan.setThrowable(e);
    innerSpan.setStatus(SpanStatus.NOT_FOUND);
    throw e;
  } finally {
    innerSpan.finish();
  }
}
```

Keep in mind that each individual span also needs to be manually finished; otherwise, spans will not show up in the transaction.

Spans are sent together with their parent transaction when the transaction is finished. Make sure to call `finish()` on transaction once all the child spans have finished.

## [Create Transaction Bound to The Current Scope](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation.md#create-transaction-bound-to-the-current-scope)

Our SDK can bind a transaction to the scope making it accessible to every method running within this scope by calling `Sentry#startTransaction` method with `bindToScope` parameter to `true`.

Our auto instrumentation, for example those instrumenting database requests or HTTP calls, require a transaction to be bound to the current scope in order to attach a new child span.

`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 `Sentry#getSpan`. This method will return a `SentryTransaction` in case there is a running Transaction or a `Span` in case there is already a running Span, otherwise it returns `null`.

```java
import java.io.FileNotFoundException;

import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.Sentry;
import io.sentry.SpanStatus;
import io.sentry.TransactionOptions;

// A good name for the transaction is key, to help identify what this is about
TransactionOptions txOptions = new TransactionOptions();
txOptions.setBindToScope(true);
ITransaction transaction = Sentry.startTransaction("processOrderBatch()", "task", txOptions);
try {
  processOrderBatch();
} catch (Exception e) {
  transaction.setThrowable(e);
  transaction.setStatus(SpanStatus.INTERNAL_ERROR);
  throw e;
} finally {
  transaction.finish();
}

void processOrderBatch() {
  ISpan parentSpan = Sentry.getSpan(); // returns the "processOrderBatch()" transaction
  ISpan innerSpan = null;
  if (parentSpan != null) {
    innerSpan = parentSpan.startChild("task", "operation");
  }
  
  try {
    // omitted code
  } catch (FileNotFoundException e) {
    if (innerSpan != null) {
      innerSpan.setThrowable(e);
      innerSpan.setStatus(SpanStatus.NOT_FOUND);
    throw e;
  } finally {
    if (innerSpan != null) {
      innerSpan.finish();
    }
  }
}
```

## [Connect Errors with Spans](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation.md#connect-errors-with-spans)

Sentry errors can be linked with transactions and spans.

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

```java
import io.sentry.Sentry;
import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.TransactionOptions;

TransactionOptions txOptions = new TransactionOptions();
txOptions.setBindToScope(true);
ITransaction span = Sentry.startTransaction(item.getTransactionName(), "task", txOptions);
try {
  processItem();
} catch (Exception e) {
  Sentry.captureException(e);
} finally {
  span.finish();
}
```

Exceptions may be thrown within spans that can finish before exception gets reported to Sentry. To attach span information to this exception, you must link it by calling setThrowable method:

```java
import io.sentry.Sentry;
import io.sentry.ISpan;

ISpan span = Sentry.getSpan();
if (span == null) {
  span = Sentry.startTransaction(item.getTransactionName(), "task");
}
try {
  processItem();
} catch (Exception e) {
  span.setThrowable(e);
  throw e;
} finally {
  span.finish();
}
```

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

### [Adding Data Attributes to Transactions](https://docs.sentry.io/platforms/java/guides/logback/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 of type `String`, `Number` or `Boolean`, as well as (non-mixed) arrays of these types:

```java
ITransaction transaction = Sentry.startTransaction("processOrderBatch()", "task");
transaction.setData("my-data-attribute-1", "value1");
transaction.setData("my-data-attribute-2", 42);
transaction.setData("my-data-attribute-3", true);

transaction.setData("my-data-attribute-4", Arrays.asList("value1", "value2", "value3"));
transaction.setData("my-data-attribute-5", Arrays.asList(42, 43, 44));
transaction.setData("my-data-attribute-6", Arrays.asList(true, false, true));
```

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

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:

```java
ISpan span = parent.startChild("task", "operation");
span.setData("my-data-attribute-1", "value1");
span.setData("my-data-attribute-2", 42);
span.setData("my-data-attribute-3", true);

span.setData("my-data-attribute-4", Arrays.asList("value1", "value2", "value3"));
span.setData("my-data-attribute-5", Arrays.asList(42, 43, 44));
span.setData("my-data-attribute-6", Arrays.asList(true, false, true));
```

### [Adding Attributes to all Spans and Transactions](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation.md#adding-attributes-to-all-spans-and-transactions)

To add an attribute to all spans, use the `beforeSendTransaction` callback:

```java
Sentry.init(options -> {
    options.setBeforeSendTransaction((transaction, hint) -> {
        
        // set the attribute on the root span
        if (transaction.getContexts().getTrace() == null) {
            SpanContext spanContext = new SpanContext("op");
            transaction.getContexts().setTrace(spanContext);
        }
        transaction.getContexts().getTrace().setData("myAttribute", "myValue");

        // and on all child spans
        transaction.getSpans().forEach(span -> {
            if (span.getData() == null) {
                span.setData(new HashMap<>());
            }
            span.getData().put("myAttribute", "myValue");
        });

        return transaction;
    });
});
```

## Pages in this section

- [Instrument Caches](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation/caches-module.md)
- [Instrument Queues](https://docs.sentry.io/platforms/java/guides/logback/tracing/instrumentation/custom-instrumentation/queues-module.md)
