Skip to content

Commit f86fb22

Browse files
authored
Add HTTP response code to Spring WebFlux transactions (#2870)
1 parent 4c237f8 commit f86fb22

File tree

7 files changed

+48
-11
lines changed

7 files changed

+48
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features
66

7+
- Add HTTP response code to Spring WebFlux transactions ([#2870](https://github.com/getsentry/sentry-java/pull/2870))
78
- Add `sampled` to Dynamic Sampling Context ([#2869](https://github.com/getsentry/sentry-java/pull/2869))
89

910
### Fixes

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,17 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact
110110
transaction.setName(transactionName, TransactionNameSource.ROUTE);
111111
transaction.setOperation(TRANSACTION_OP);
112112
}
113-
if (transaction.getStatus() == null) {
114-
final @Nullable ServerHttpResponse response = exchange.getResponse();
115-
if (response != null) {
116-
final @Nullable HttpStatusCode statusCode = response.getStatusCode();
117-
if (statusCode != null) {
113+
final @Nullable ServerHttpResponse response = exchange.getResponse();
114+
if (response != null) {
115+
final @Nullable HttpStatusCode statusCode = response.getStatusCode();
116+
if (statusCode != null) {
117+
transaction
118+
.getContexts()
119+
.withResponse(
120+
(sentryResponse) -> {
121+
sentryResponse.setStatusCode(statusCode.value());
122+
});
123+
if (transaction.getStatus() == null) {
118124
transaction.setStatus(SpanStatus.fromHttpStatusCode(statusCode.value()));
119125
}
120126
}

sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class SentryWebFluxTracingFilterTest {
115115
assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK)
116116
assertThat(it.contexts.trace!!.operation).isEqualTo("http.server")
117117
assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring_jakarta.webflux")
118+
assertThat(it.contexts.response!!.statusCode).isEqualTo(200)
118119
},
119120
anyOrNull<TraceContext>(),
120121
anyOrNull(),
@@ -133,6 +134,7 @@ class SentryWebFluxTracingFilterTest {
133134
verify(fixture.hub).captureTransaction(
134135
check {
135136
assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR)
137+
assertThat(it.contexts.response!!.statusCode).isEqualTo(500)
136138
},
137139
anyOrNull<TraceContext>(),
138140
anyOrNull(),

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,17 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact
144144
transaction.setName(transactionName, TransactionNameSource.ROUTE);
145145
transaction.setOperation(TRANSACTION_OP);
146146
}
147-
if (transaction.getStatus() == null) {
148-
final @Nullable ServerHttpResponse response = exchange.getResponse();
149-
if (response != null) {
150-
final @Nullable Integer rawStatusCode = response.getRawStatusCode();
151-
if (rawStatusCode != null) {
147+
final @Nullable ServerHttpResponse response = exchange.getResponse();
148+
if (response != null) {
149+
final @Nullable Integer rawStatusCode = response.getRawStatusCode();
150+
if (rawStatusCode != null) {
151+
transaction
152+
.getContexts()
153+
.withResponse(
154+
(sentryResponse) -> {
155+
sentryResponse.setStatusCode(rawStatusCode);
156+
});
157+
if (transaction.getStatus() == null) {
152158
transaction.setStatus(SpanStatus.fromHttpStatusCode(rawStatusCode));
153159
}
154160
}

sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class SentryWebFluxTracingFilterTest {
116116
assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK)
117117
assertThat(it.contexts.trace!!.operation).isEqualTo("http.server")
118118
assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring.webflux")
119+
assertThat(it.contexts.response!!.statusCode).isEqualTo(200)
119120
},
120121
anyOrNull<TraceContext>(),
121122
anyOrNull(),
@@ -134,6 +135,7 @@ class SentryWebFluxTracingFilterTest {
134135
verify(fixture.hub).captureTransaction(
135136
check {
136137
assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR)
138+
assertThat(it.contexts.response!!.statusCode).isEqualTo(500)
137139
},
138140
anyOrNull<TraceContext>(),
139141
anyOrNull(),

sentry/api/sentry.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3009,6 +3009,7 @@ public final class io/sentry/protocol/Contexts : java/util/concurrent/Concurrent
30093009
public fun setResponse (Lio/sentry/protocol/Response;)V
30103010
public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V
30113011
public fun setTrace (Lio/sentry/SpanContext;)V
3012+
public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V
30123013
}
30133014

30143015
public final class io/sentry/protocol/Contexts$Deserializer : io/sentry/JsonDeserializer {

sentry/src/main/java/io/sentry/protocol/Contexts.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.sentry.JsonSerializable;
77
import io.sentry.ObjectWriter;
88
import io.sentry.SpanContext;
9+
import io.sentry.util.HintUtils;
910
import io.sentry.util.Objects;
1011
import io.sentry.vendor.gson.stream.JsonToken;
1112
import java.io.IOException;
@@ -19,6 +20,9 @@
1920
public final class Contexts extends ConcurrentHashMap<String, Object> implements JsonSerializable {
2021
private static final long serialVersionUID = 252445813254943011L;
2122

23+
/** Response lock, Ops should be atomic */
24+
private final @NotNull Object responseLock = new Object();
25+
2226
public Contexts() {}
2327

2428
public Contexts(final @NotNull Contexts contexts) {
@@ -115,8 +119,23 @@ public void setGpu(final @NotNull Gpu gpu) {
115119
return toContextType(Response.TYPE, Response.class);
116120
}
117121

122+
public void withResponse(HintUtils.SentryConsumer<Response> callback) {
123+
synchronized (responseLock) {
124+
final @Nullable Response response = getResponse();
125+
if (response != null) {
126+
callback.accept(response);
127+
} else {
128+
final @NotNull Response newResponse = new Response();
129+
setResponse(newResponse);
130+
callback.accept(newResponse);
131+
}
132+
}
133+
}
134+
118135
public void setResponse(final @NotNull Response response) {
119-
this.put(Response.TYPE, response);
136+
synchronized (responseLock) {
137+
this.put(Response.TYPE, response);
138+
}
120139
}
121140

122141
// region json

0 commit comments

Comments
 (0)