Skip to content

Commit 1ba2a69

Browse files
authored
Merge 8324fd8 into 1707044
2 parents 1707044 + 8324fd8 commit 1ba2a69

File tree

12 files changed

+233
-292
lines changed

12 files changed

+233
-292
lines changed

sentry-spring-boot-starter-jakarta/api/sentry-spring-boot-starter-jakarta.api

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,5 @@ public class io/sentry/spring/boot/jakarta/SentryProperties$Reactive {
5353
public class io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration {
5454
public fun <init> ()V
5555
public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/jakarta/webflux/SentryWebExceptionHandler;
56-
public fun sentryWebTracingFilter ()Lio/sentry/spring/jakarta/webflux/SentryWebTracingFilter;
5756
}
5857

sentry-spring-boot-starter-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
import io.sentry.spring.jakarta.webflux.SentryWebExceptionHandler;
77
import io.sentry.spring.jakarta.webflux.SentryWebFilter;
88
import io.sentry.spring.jakarta.webflux.SentryWebFilterWithThreadLocalAccessor;
9-
import io.sentry.spring.jakarta.webflux.SentryWebTracingFilter;
109
import org.jetbrains.annotations.ApiStatus;
1110
import org.jetbrains.annotations.NotNull;
1211
import org.springframework.boot.ApplicationRunner;
1312
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
1413
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
1514
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
1615
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
17-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1816
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
1917
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2018
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@@ -77,14 +75,6 @@ static class SentryWebfluxFilterConfiguration {
7775
}
7876
}
7977

80-
@Bean
81-
@Order(SENTRY_SPRING_FILTER_PRECEDENCE + 1)
82-
@Conditional(SentryAutoConfiguration.SentryTracingCondition.class)
83-
@ConditionalOnMissingBean(name = "sentryWebTracingFilter")
84-
public @NotNull SentryWebTracingFilter sentryWebTracingFilter() {
85-
return new SentryWebTracingFilter();
86-
}
87-
8878
/** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */
8979
@Bean
9080
public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) {

sentry-spring-boot-starter/api/sentry-spring-boot-starter.api

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,5 @@ public class io/sentry/spring/boot/SentryWebfluxAutoConfiguration {
4747
public fun sentryScheduleHookApplicationRunner ()Lorg/springframework/boot/ApplicationRunner;
4848
public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebExceptionHandler;
4949
public fun sentryWebFilter (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebFilter;
50-
public fun sentryWebTracingFilter ()Lio/sentry/spring/webflux/SentryWebTracingFilter;
5150
}
5251

sentry-spring-boot-starter/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@
55
import io.sentry.spring.webflux.SentryScheduleHook;
66
import io.sentry.spring.webflux.SentryWebExceptionHandler;
77
import io.sentry.spring.webflux.SentryWebFilter;
8-
import io.sentry.spring.webflux.SentryWebTracingFilter;
98
import org.jetbrains.annotations.ApiStatus;
109
import org.jetbrains.annotations.NotNull;
1110
import org.springframework.boot.ApplicationRunner;
1211
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
1312
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
14-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1513
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
1614
import org.springframework.context.annotation.Bean;
17-
import org.springframework.context.annotation.Conditional;
1815
import org.springframework.context.annotation.Configuration;
1916
import org.springframework.core.Ordered;
2017
import org.springframework.core.annotation.Order;
@@ -45,14 +42,6 @@ public class SentryWebfluxAutoConfiguration {
4542
return new SentryWebFilter(hub);
4643
}
4744

48-
@Bean
49-
@Order(SENTRY_SPRING_FILTER_PRECEDENCE + 1)
50-
@Conditional(SentryAutoConfiguration.SentryTracingCondition.class)
51-
@ConditionalOnMissingBean(name = "sentryWebTracingFilter")
52-
public @NotNull SentryWebTracingFilter sentryWebTracingFilter() {
53-
return new SentryWebTracingFilter();
54-
}
55-
5645
/** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */
5746
@Bean
5847
public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) {

sentry-spring-jakarta/api/sentry-spring-jakarta.api

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,12 @@ public abstract interface class io/sentry/spring/jakarta/tracing/TransactionName
163163
public abstract class io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter : org/springframework/web/server/WebFilter {
164164
public static final field SENTRY_HUB_KEY Ljava/lang/String;
165165
public fun <init> (Lio/sentry/IHub;)V
166-
protected fun doFinally (Lio/sentry/IHub;)V
166+
protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;Lio/sentry/ITransaction;)V
167167
protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;)V
168+
protected fun doOnError (Lio/sentry/ITransaction;Ljava/lang/Throwable;)V
169+
protected fun maybeStartTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction;
170+
protected fun shouldTraceRequest (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z
171+
protected fun startTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction;
168172
}
169173

170174
public final class io/sentry/spring/jakarta/webflux/ReactorUtils {
@@ -215,8 +219,3 @@ public final class io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLoc
215219
public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono;
216220
}
217221

218-
public class io/sentry/spring/jakarta/webflux/SentryWebTracingFilter : org/springframework/web/server/WebFilter {
219-
public fun <init> ()V
220-
public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono;
221-
}
222-

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,30 @@
33
import static io.sentry.TypeCheckHint.WEBFLUX_FILTER_REQUEST;
44
import static io.sentry.TypeCheckHint.WEBFLUX_FILTER_RESPONSE;
55

6+
import io.sentry.Baggage;
7+
import io.sentry.BaggageHeader;
68
import io.sentry.Breadcrumb;
9+
import io.sentry.CustomSamplingContext;
710
import io.sentry.Hint;
811
import io.sentry.IHub;
12+
import io.sentry.ITransaction;
13+
import io.sentry.NoOpHub;
14+
import io.sentry.Sentry;
15+
import io.sentry.SentryLevel;
16+
import io.sentry.SentryTraceHeader;
17+
import io.sentry.SpanStatus;
18+
import io.sentry.TransactionContext;
19+
import io.sentry.TransactionOptions;
20+
import io.sentry.exception.InvalidSentryTraceHeaderException;
21+
import io.sentry.protocol.TransactionNameSource;
922
import io.sentry.util.Objects;
23+
import java.util.List;
1024
import org.jetbrains.annotations.ApiStatus;
1125
import org.jetbrains.annotations.NotNull;
26+
import org.jetbrains.annotations.Nullable;
27+
import org.springframework.http.HttpHeaders;
28+
import org.springframework.http.HttpMethod;
29+
import org.springframework.http.HttpStatusCode;
1230
import org.springframework.http.server.reactive.ServerHttpRequest;
1331
import org.springframework.http.server.reactive.ServerHttpResponse;
1432
import org.springframework.web.server.ServerWebExchange;
@@ -19,14 +37,33 @@
1937
public abstract class AbstractSentryWebFilter implements WebFilter {
2038
private final @NotNull SentryRequestResolver sentryRequestResolver;
2139
public static final String SENTRY_HUB_KEY = "sentry-hub";
40+
private static final String TRANSACTION_OP = "http.server";
2241

2342
public AbstractSentryWebFilter(final @NotNull IHub hub) {
2443
Objects.requireNonNull(hub, "hub is required");
2544
this.sentryRequestResolver = new SentryRequestResolver(hub);
2645
}
2746

28-
protected void doFinally(final @NotNull IHub requestHub) {
47+
protected @Nullable ITransaction maybeStartTransaction(
48+
final @NotNull IHub requestHub, final @NotNull ServerHttpRequest request) {
49+
if (requestHub.isEnabled()
50+
&& requestHub.getOptions().isTracingEnabled()
51+
&& shouldTraceRequest(requestHub, request)) {
52+
return startTransaction(requestHub, request);
53+
} else {
54+
return null;
55+
}
56+
}
57+
58+
protected void doFinally(
59+
final @NotNull ServerWebExchange serverWebExchange,
60+
final @NotNull IHub requestHub,
61+
final @Nullable ITransaction transaction) {
62+
if (transaction != null) {
63+
finishTransaction(serverWebExchange, transaction);
64+
}
2965
requestHub.popScope();
66+
Sentry.setCurrentHub(NoOpHub.getInstance());
3067
}
3168

3269
protected void doFirst(
@@ -44,4 +81,75 @@ protected void doFirst(
4481
requestHub.configureScope(
4582
scope -> scope.setRequest(sentryRequestResolver.resolveSentryRequest(request)));
4683
}
84+
85+
protected void doOnError(final @Nullable ITransaction transaction, final @NotNull Throwable e) {
86+
if (transaction != null) {
87+
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
88+
transaction.setThrowable(e);
89+
}
90+
}
91+
92+
protected boolean shouldTraceRequest(
93+
final @NotNull IHub hub, final @NotNull ServerHttpRequest request) {
94+
return hub.getOptions().isTraceOptionsRequests()
95+
|| !HttpMethod.OPTIONS.equals(request.getMethod());
96+
}
97+
98+
private void finishTransaction(ServerWebExchange exchange, ITransaction transaction) {
99+
String transactionName = TransactionNameProvider.provideTransactionName(exchange);
100+
if (transactionName != null) {
101+
transaction.setName(transactionName, TransactionNameSource.ROUTE);
102+
transaction.setOperation(TRANSACTION_OP);
103+
}
104+
if (transaction.getStatus() == null) {
105+
final @Nullable ServerHttpResponse response = exchange.getResponse();
106+
if (response != null) {
107+
final @Nullable HttpStatusCode statusCode = response.getStatusCode();
108+
if (statusCode != null) {
109+
transaction.setStatus(SpanStatus.fromHttpStatusCode(statusCode.value()));
110+
}
111+
}
112+
}
113+
transaction.finish();
114+
}
115+
116+
protected @NotNull ITransaction startTransaction(
117+
final @NotNull IHub hub, final @NotNull ServerHttpRequest request) {
118+
final @NotNull HttpHeaders headers = request.getHeaders();
119+
final @Nullable List<String> sentryTraceHeaders =
120+
headers.get(SentryTraceHeader.SENTRY_TRACE_HEADER);
121+
final @Nullable List<String> baggageHeaders = headers.get(BaggageHeader.BAGGAGE_HEADER);
122+
final @NotNull String name = request.getMethod() + " " + request.getURI().getPath();
123+
final @NotNull CustomSamplingContext customSamplingContext = new CustomSamplingContext();
124+
customSamplingContext.set("request", request);
125+
126+
final TransactionOptions transactionOptions = new TransactionOptions();
127+
transactionOptions.setCustomSamplingContext(customSamplingContext);
128+
transactionOptions.setBindToScope(true);
129+
130+
if (sentryTraceHeaders != null && sentryTraceHeaders.size() > 0) {
131+
final @NotNull Baggage baggage =
132+
Baggage.fromHeader(baggageHeaders, hub.getOptions().getLogger());
133+
try {
134+
final @NotNull TransactionContext contexts =
135+
TransactionContext.fromSentryTrace(
136+
name,
137+
TransactionNameSource.URL,
138+
TRANSACTION_OP,
139+
new SentryTraceHeader(sentryTraceHeaders.get(0)),
140+
baggage,
141+
null);
142+
143+
return hub.startTransaction(contexts, transactionOptions);
144+
} catch (InvalidSentryTraceHeaderException e) {
145+
hub.getOptions()
146+
.getLogger()
147+
.log(SentryLevel.DEBUG, e, "Failed to parse Sentry trace header: %s", e.getMessage());
148+
}
149+
}
150+
151+
return hub.startTransaction(
152+
new TransactionContext(name, TransactionNameSource.URL, TRANSACTION_OP),
153+
transactionOptions);
154+
}
47155
}

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.jakewharton.nopen.annotation.Open;
44
import io.sentry.IHub;
5-
import io.sentry.NoOpHub;
5+
import io.sentry.ITransaction;
66
import io.sentry.Sentry;
77
import org.jetbrains.annotations.ApiStatus;
88
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
import org.springframework.http.server.reactive.ServerHttpRequest;
911
import org.springframework.web.server.ServerWebExchange;
1012
import org.springframework.web.server.WebFilterChain;
1113
import reactor.core.publisher.Mono;
@@ -24,13 +26,12 @@ public Mono<Void> filter(
2426
final @NotNull ServerWebExchange serverWebExchange,
2527
final @NotNull WebFilterChain webFilterChain) {
2628
@NotNull IHub requestHub = Sentry.cloneMainHub();
29+
final ServerHttpRequest request = serverWebExchange.getRequest();
30+
final @Nullable ITransaction transaction = maybeStartTransaction(requestHub, request);
2731
return webFilterChain
2832
.filter(serverWebExchange)
29-
.doFinally(
30-
__ -> {
31-
doFinally(requestHub);
32-
Sentry.setCurrentHub(NoOpHub.getInstance());
33-
})
33+
.doFinally(__ -> doFinally(serverWebExchange, requestHub, transaction))
34+
.doOnError(e -> doOnError(transaction, e))
3435
.doFirst(
3536
() -> {
3637
Sentry.setCurrentHub(requestHub);

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package io.sentry.spring.jakarta.webflux;
22

33
import io.sentry.IHub;
4-
import io.sentry.NoOpHub;
4+
import io.sentry.ITransaction;
55
import io.sentry.Sentry;
66
import org.jetbrains.annotations.ApiStatus;
77
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
89
import org.springframework.web.server.ServerWebExchange;
910
import org.springframework.web.server.WebFilterChain;
1011
import reactor.core.publisher.Mono;
@@ -21,17 +22,26 @@ public SentryWebFilterWithThreadLocalAccessor(final @NotNull IHub hub) {
2122
public Mono<Void> filter(
2223
final @NotNull ServerWebExchange serverWebExchange,
2324
final @NotNull WebFilterChain webFilterChain) {
25+
final @NotNull TransactionContainer transactionContainer = new TransactionContainer();
2426
return ReactorUtils.withSentryNewMainHubClone(
2527
webFilterChain
2628
.filter(serverWebExchange)
2729
.doFinally(
28-
__ -> {
29-
doFinally(Sentry.getCurrentHub());
30-
Sentry.setCurrentHub(NoOpHub.getInstance());
31-
})
30+
__ ->
31+
doFinally(
32+
serverWebExchange,
33+
Sentry.getCurrentHub(),
34+
transactionContainer.transaction))
35+
.doOnError(e -> doOnError(transactionContainer.transaction, e))
3236
.doFirst(
3337
() -> {
3438
doFirst(serverWebExchange, Sentry.getCurrentHub());
39+
transactionContainer.transaction =
40+
maybeStartTransaction(Sentry.getCurrentHub(), serverWebExchange.getRequest());
3541
}));
3642
}
43+
44+
private static class TransactionContainer {
45+
private volatile @Nullable ITransaction transaction;
46+
}
3747
}

0 commit comments

Comments
 (0)