Custom Instrumentation
To capture transactions customized to your organization's needs, you must first set up performance monitoring.
To instrument certain regions of your code, you can create transactions to capture them.
Capturing Bean Method Execution
Every Spring bean method execution can be turned into a transaction or a span.
Capturing Transaction
To enable this feature, you must include spring-aop
and aspectj
in your application:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
Then, import SentryTracingConfiguration
in one of your @Configuration
classes:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import io.sentry.spring.tracing.SentryTracingConfiguration;
@Import(SentryTracingConfiguration.class)
class SentryConfig {
}
Methods executed outside of Spring MVC request processing can be turned into transactions by annotating them with @SentryTransaction
annotation:
import org.springframework.stereotype.Component;
import org.springframework.scheduling.annotation.Scheduled;
import io.sentry.spring.tracing.SentryTransaction;
@Component
class ScheduledJob {
@Scheduled(...)
@SentryTransaction(operation = "task")
void execute() {
...
}
}
@SentryTransaction
can be configured with custom name
. If not defined, name
will be resolved from the class, and the method name.
@SentryTransaction
can be also placed on a class or an interface - it turns every method execution from the annotated type into a transaction.
Advanced Spring AOP users can redefine transaction pointcut by providing a custom org.springframework.aop.Pointcut
bean with name sentryTransactionPointcut
.
Capturing a Span
To create a span around a method execution, annotate method with @SentrySpan
annotation:
import org.springframework.stereotype.Component;
import io.sentry.spring.tracing.SentrySpan;
@Component
class PersonService {
@SentrySpan
Person findById(Long id) {
...
}
}
@SentrySpan
can be configured with custom description
property. If not defined, operation
will be resolved from the class, and the method name.
@SentrySpan
can be also placed on a class or an interface - it turns every method execution from the annotated type into a span.
Advanced Spring AOP users can redefine around which methods spans and transactions are created by creating custom advice, pointcut and advisor beans instead of importing SentryTracingConfiguration
class.
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
.
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 span) {
if (span == null) {
span = Sentry.startTransaction("processOrderBatch()", "task");
}
// span operation: task, span description: operation
ISpan innerSpan = span.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
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
.
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
.
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", true);
try {
processOrderBatch();
} catch (Exception e) {
transaction.setThrowable(e);
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
throw e;
} finally {
transaction.finish();
}
void processOrderBatch() {
ISpan span = Sentry.getSpan();
if (span == null) {
span = Sentry.startTransaction("processOrderBatch()", "task");
}
ISpan innerSpan = span.startChild("task", "operation");
try {
// omitted code
} catch (FileNotFoundException e) {
innerSpan.setThrowable(e);
innerSpan.setStatus(SpanStatus.NOT_FOUND);
throw e;
} finally {
innerSpan.finish();
}
}
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:
import io.sentry.Sentry;
import io.sentry.ISpan;
ITransaction span = Sentry.startTransaction(item.getTransactionName(), "task", true);
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:
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();
}
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 a trace header from the span, use ISpan#toSentryTrace()
method. Then pass it to the downstream service. If the communication happens over HTTP, it is recommended that you set the value to the sentry-trace
(also available as a constant SentryTraceHeader.SENTRY_TRACE_HEADER
) HTTP header.
To create a span as a continuation of the trace retrieved from the upstream service, pass the sentry-trace
header value to the transaction context:
import io.sentry.ITransaction;
import io.sentry.Sentry;
import io.sentry.SentryTraceHeader;
import io.sentry.TransactionContext;
import io.sentry.exception.InvalidSentryTraceHeaderException;
String sentryTrace = request.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER);
ITransaction transaction = null;
try {
transaction = Sentry.startTransaction(TransactionContext.fromSentryTrace("name", "op", new SentryTraceHeader(sentryTrace)));
} catch (InvalidSentryTraceHeaderException e) {
// handle invalid trace header
}
In Spring and Spring Boot integrations, Spring MVC, RestTemplate
and OkHttp instrumentation handles trace continuations automatically.
- Package:
- maven:io.sentry:sentry-spring
- Version:
- 7.13.0
- Repository:
- https://github.com/getsentry/sentry-java