Skip to content

Commit 656ee6f

Browse files
committed
Add support for async dispatch requests
Keeps the transaction open until the response is committed.
1 parent e227906 commit 656ee6f

File tree

2 files changed

+80
-53
lines changed

2 files changed

+80
-53
lines changed

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class SentryTracingFilter extends OncePerRequestFilter {
3636
private static final String TRANSACTION_OP = "http.server";
3737

3838
private static final String TRACE_ORIGIN = "auto.http.spring_jakarta.webmvc";
39+
private static final String TRANSACTION_ATTR = AsyncSentryTracingFilter.class.getName() + ".transaction";
3940

4041
private final @NotNull TransactionNameProvider transactionNameProvider;
4142
private final @NotNull IHub hub;
@@ -52,6 +53,11 @@ public SentryTracingFilter() {
5253
this(HubAdapter.getInstance());
5354
}
5455

56+
@Override
57+
protected boolean shouldNotFilterAsyncDispatch() {
58+
return false;
59+
}
60+
5561
/**
5662
* Creates filter that resolves transaction name using transaction name provider given by
5763
* parameter.
@@ -77,14 +83,8 @@ protected void doFilterInternal(
7783
final @NotNull FilterChain filterChain)
7884
throws ServletException, IOException {
7985
if (hub.isEnabled()) {
80-
final @Nullable String sentryTraceHeader =
81-
httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER);
82-
final @Nullable List<String> baggageHeader =
83-
Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER));
84-
final @Nullable TransactionContext transactionContext =
85-
hub.continueTrace(sentryTraceHeader, baggageHeader);
8686
if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) {
87-
doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext);
87+
doFilterWithTransaction(httpRequest, httpResponse, filterChain);
8888
} else {
8989
filterChain.doFilter(httpRequest, httpResponse);
9090
}
@@ -96,12 +96,24 @@ protected void doFilterInternal(
9696
private void doFilterWithTransaction(
9797
HttpServletRequest httpRequest,
9898
HttpServletResponse httpResponse,
99-
FilterChain filterChain,
100-
final @Nullable TransactionContext transactionContext)
99+
FilterChain filterChain)
101100
throws IOException, ServletException {
102-
// at this stage we are not able to get real transaction name
103-
final ITransaction transaction = startTransaction(httpRequest, transactionContext);
104-
transaction.getSpanContext().setOrigin(TRACE_ORIGIN);
101+
final ITransaction transaction;
102+
if (isAsyncDispatch(httpRequest)) {
103+
transaction = (ITransaction) httpRequest.getAttribute(TRANSACTION_ATTR);
104+
} else {
105+
final @Nullable String sentryTraceHeader =
106+
httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER);
107+
final @Nullable List<String> baggageHeader =
108+
Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER));
109+
final @Nullable TransactionContext transactionContext =
110+
hub.continueTrace(sentryTraceHeader, baggageHeader);
111+
112+
// at this stage we are not able to get real transaction name
113+
transaction = startTransaction(httpRequest, transactionContext);
114+
transaction.getSpanContext().setOrigin(TRACE_ORIGIN);
115+
httpRequest.setAttribute(TRANSACTION_ATTR, transaction);
116+
}
105117

106118
try {
107119
filterChain.doFilter(httpRequest, httpResponse);
@@ -110,21 +122,23 @@ private void doFilterWithTransaction(
110122
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
111123
throw e;
112124
} finally {
113-
// after all filters run, templated path pattern is available in request attribute
114-
final String transactionName = transactionNameProvider.provideTransactionName(httpRequest);
115-
final TransactionNameSource transactionNameSource =
116-
transactionNameProvider.provideTransactionSource();
117-
// if transaction name is not resolved, the request has not been processed by a controller
118-
// and we should not report it to Sentry
119-
if (transactionName != null) {
120-
transaction.setName(transactionName, transactionNameSource);
121-
transaction.setOperation(TRANSACTION_OP);
122-
// if exception has been thrown, transaction status is already set to INTERNAL_ERROR, and
123-
// httpResponse.getStatus() returns 200.
124-
if (transaction.getStatus() == null) {
125-
transaction.setStatus(SpanStatus.fromHttpStatusCode(httpResponse.getStatus()));
125+
if (!isAsyncStarted(httpRequest)) {
126+
// after all filters run, templated path pattern is available in request attribute
127+
final String transactionName = transactionNameProvider.provideTransactionName(httpRequest);
128+
final TransactionNameSource transactionNameSource =
129+
transactionNameProvider.provideTransactionSource();
130+
// if transaction name is not resolved, the request has not been processed by a controller
131+
// and we should not report it to Sentry
132+
if (transactionName != null) {
133+
transaction.setName(transactionName, transactionNameSource);
134+
transaction.setOperation(TRANSACTION_OP);
135+
// if exception has been thrown, transaction status is already set to INTERNAL_ERROR, and
136+
// httpResponse.getStatus() returns 200.
137+
if (transaction.getStatus() == null) {
138+
transaction.setStatus(SpanStatus.fromHttpStatusCode(httpResponse.getStatus()));
139+
}
140+
transaction.finish();
126141
}
127-
transaction.finish();
128142
}
129143
}
130144
}

sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class SentryTracingFilter extends OncePerRequestFilter {
3333
private static final String TRANSACTION_OP = "http.server";
3434

3535
private static final String TRACE_ORIGIN = "auto.http.spring.webmvc";
36+
private static final String TRANSACTION_ATTR = AsyncSentryTracingFilter.class.getName() + ".transaction";
3637

3738
private final @NotNull TransactionNameProvider transactionNameProvider;
3839
private final @NotNull IHub hub;
@@ -67,6 +68,11 @@ public SentryTracingFilter(final @NotNull IHub hub) {
6768
this(hub, new SpringMvcTransactionNameProvider());
6869
}
6970

71+
@Override
72+
protected boolean shouldNotFilterAsyncDispatch() {
73+
return false;
74+
}
75+
7076
@Override
7177
protected void doFilterInternal(
7278
final @NotNull HttpServletRequest httpRequest,
@@ -75,15 +81,8 @@ protected void doFilterInternal(
7581
throws ServletException, IOException {
7682

7783
if (hub.isEnabled()) {
78-
final @Nullable String sentryTraceHeader =
79-
httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER);
80-
final @Nullable List<String> baggageHeader =
81-
Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER));
82-
final @Nullable TransactionContext transactionContext =
83-
hub.continueTrace(sentryTraceHeader, baggageHeader);
84-
8584
if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) {
86-
doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext);
85+
doFilterWithTransaction(httpRequest, httpResponse, filterChain);
8786
} else {
8887
filterChain.doFilter(httpRequest, httpResponse);
8988
}
@@ -95,12 +94,24 @@ protected void doFilterInternal(
9594
private void doFilterWithTransaction(
9695
HttpServletRequest httpRequest,
9796
HttpServletResponse httpResponse,
98-
FilterChain filterChain,
99-
final @Nullable TransactionContext transactionContext)
97+
FilterChain filterChain)
10098
throws IOException, ServletException {
101-
// at this stage we are not able to get real transaction name
102-
final ITransaction transaction = startTransaction(httpRequest, transactionContext);
103-
transaction.getSpanContext().setOrigin(TRACE_ORIGIN);
99+
final ITransaction transaction;
100+
if (isAsyncDispatch(httpRequest)) {
101+
transaction = (ITransaction) httpRequest.getAttribute(TRANSACTION_ATTR);
102+
} else {
103+
final @Nullable String sentryTraceHeader =
104+
httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER);
105+
final @Nullable List<String> baggageHeader =
106+
Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER));
107+
final @Nullable TransactionContext transactionContext =
108+
hub.continueTrace(sentryTraceHeader, baggageHeader);
109+
110+
// at this stage we are not able to get real transaction name
111+
transaction = startTransaction(httpRequest, transactionContext);
112+
transaction.getSpanContext().setOrigin(TRACE_ORIGIN);
113+
httpRequest.setAttribute(TRANSACTION_ATTR, transaction);
114+
}
104115

105116
try {
106117
filterChain.doFilter(httpRequest, httpResponse);
@@ -109,21 +120,23 @@ private void doFilterWithTransaction(
109120
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
110121
throw e;
111122
} finally {
112-
// after all filters run, templated path pattern is available in request attribute
113-
final String transactionName = transactionNameProvider.provideTransactionName(httpRequest);
114-
final TransactionNameSource transactionNameSource =
115-
transactionNameProvider.provideTransactionSource();
116-
// if transaction name is not resolved, the request has not been processed by a controller
117-
// and we should not report it to Sentry
118-
if (transactionName != null) {
119-
transaction.setName(transactionName, transactionNameSource);
120-
transaction.setOperation(TRANSACTION_OP);
121-
// if exception has been thrown, transaction status is already set to INTERNAL_ERROR, and
122-
// httpResponse.getStatus() returns 200.
123-
if (transaction.getStatus() == null) {
124-
transaction.setStatus(SpanStatus.fromHttpStatusCode(httpResponse.getStatus()));
123+
if (!isAsyncStarted(httpRequest)) {
124+
// after all filters run, templated path pattern is available in request attribute
125+
final String transactionName = transactionNameProvider.provideTransactionName(httpRequest);
126+
final TransactionNameSource transactionNameSource =
127+
transactionNameProvider.provideTransactionSource();
128+
// if transaction name is not resolved, the request has not been processed by a controller
129+
// and we should not report it to Sentry
130+
if (transactionName != null) {
131+
transaction.setName(transactionName, transactionNameSource);
132+
transaction.setOperation(TRANSACTION_OP);
133+
// if exception has been thrown, transaction status is already set to INTERNAL_ERROR, and
134+
// httpResponse.getStatus() returns 200.
135+
if (transaction.getStatus() == null) {
136+
transaction.setStatus(SpanStatus.fromHttpStatusCode(httpResponse.getStatus()));
137+
}
138+
transaction.finish();
125139
}
126-
transaction.finish();
127140
}
128141
}
129142
}

0 commit comments

Comments
 (0)