io.sentry.spring.jakarta.tracing.SentryTransactionAdvice Maven / Gradle / Ivy
package io.sentry.spring.jakarta.tracing;
import com.jakewharton.nopen.annotation.Open;
import io.sentry.IScopes;
import io.sentry.ISentryLifecycleToken;
import io.sentry.ITransaction;
import io.sentry.ScopesAdapter;
import io.sentry.SpanStatus;
import io.sentry.TransactionContext;
import io.sentry.TransactionOptions;
import io.sentry.protocol.TransactionNameSource;
import io.sentry.util.Objects;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
/**
* Reports execution of every bean method annotated with {@link SentryTransaction} or a execution of
* a bean method within a class annotated with {@link SentryTransaction}.
*/
@ApiStatus.Internal
@Open
public class SentryTransactionAdvice implements MethodInterceptor {
private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice";
private final @NotNull IScopes scopesBeforeForking;
public SentryTransactionAdvice() {
this(ScopesAdapter.getInstance());
}
public SentryTransactionAdvice(final @NotNull IScopes scopes) {
this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required");
}
@SuppressWarnings("deprecation")
@Override
public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable {
final Method mostSpecificMethod =
AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass());
@Nullable
SentryTransaction sentryTransaction =
AnnotationUtils.findAnnotation(mostSpecificMethod, SentryTransaction.class);
if (sentryTransaction == null) {
sentryTransaction =
AnnotationUtils.findAnnotation(
mostSpecificMethod.getDeclaringClass(), SentryTransaction.class);
}
final TransactionNameAndSource nameAndSource =
resolveTransactionName(invocation, sentryTransaction);
final boolean isTransactionActive = isTransactionActive(scopesBeforeForking);
if (isTransactionActive) {
// transaction is already active, we do not start new transaction
return invocation.proceed();
} else {
String operation;
if (sentryTransaction != null && !StringUtils.isEmpty(sentryTransaction.operation())) {
operation = sentryTransaction.operation();
} else {
operation = "bean";
}
final @NotNull IScopes forkedScopes =
scopesBeforeForking.forkedScopes("SentryTransactionAdvice");
try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) {
final TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.setBindToScope(true);
transactionOptions.setOrigin(TRACE_ORIGIN);
final ITransaction transaction =
forkedScopes.startTransaction(
new TransactionContext(nameAndSource.name, nameAndSource.source, operation),
transactionOptions);
try {
final Object result = invocation.proceed();
transaction.setStatus(SpanStatus.OK);
return result;
} catch (Throwable e) {
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
transaction.setThrowable(e);
throw e;
} finally {
transaction.finish();
}
}
}
}
@SuppressWarnings("deprecation")
private @NotNull TransactionNameAndSource resolveTransactionName(
MethodInvocation invocation, @Nullable SentryTransaction sentryTransaction) {
if (sentryTransaction == null || StringUtils.isEmpty(sentryTransaction.value())) {
final String name =
invocation.getMethod().getDeclaringClass().getSimpleName()
+ "."
+ invocation.getMethod().getName();
return new TransactionNameAndSource(name, TransactionNameSource.COMPONENT);
} else {
return new TransactionNameAndSource(sentryTransaction.value(), TransactionNameSource.CUSTOM);
}
}
private boolean isTransactionActive(final @NotNull IScopes scopes) {
return scopes.getSpan() != null;
}
private static class TransactionNameAndSource {
private final @NotNull String name;
private final @NotNull TransactionNameSource source;
public TransactionNameAndSource(
final @NotNull String name, final @NotNull TransactionNameSource source) {
this.name = name;
this.source = source;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy