Skip to content

Commit e5bbb00

Browse files
authored
Implement ThreadLocalAccessor for propagating Sentry hub with reactor / WebFlux (#2570)
1 parent d691d8f commit e5bbb00

File tree

26 files changed

+666
-39
lines changed

26 files changed

+666
-39
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@
1111
- If set to `false` performance is disabled, regardless of `tracesSampleRate` and `tracesSampler` options.
1212
- Detect dependencies by listing MANIFEST.MF files at runtime ([#2538](https://github.com/getsentry/sentry-java/pull/2538))
1313
- Report integrations in use, report packages in use more consistently ([#2179](https://github.com/getsentry/sentry-java/pull/2179))
14+
- Implement `ThreadLocalAccessor` for propagating Sentry hub with reactor / WebFlux ([#2570](https://github.com/getsentry/sentry-java/pull/2570))
15+
- Requires `io.micrometer:context-propagation:1.0.2+` as well as Spring Boot 3.0.3+
16+
- Enable the feature by setting `sentry.reactive.thread-local-accessor-enabled=true`
17+
- This is still considered experimental. Once we have enough feedback we may turn this on by default.
18+
- Checkout the sample here: https://github.com/getsentry/sentry-java/tree/main/sentry-samples/sentry-samples-spring-boot-webflux-jakarta
19+
- A new hub is now cloned from the main hub for every request
1420

1521
### Fixes
1622

1723
- Leave `inApp` flag for stack frames undecided in SDK if unsure and let ingestion decide instead ([#2547](https://github.com/getsentry/sentry-java/pull/2547))
1824
- Allow `0.0` error sample rate ([#2573](https://github.com/getsentry/sentry-java/pull/2573))
25+
- Use the same hub in WebFlux exception handler as we do in WebFilter ([#2566](https://github.com/getsentry/sentry-java/pull/2566))
1926

2027
## 6.14.0
2128

buildSrc/src/main/java/Config.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object Config {
66
val kotlinStdLib = "stdlib-jdk8"
77

88
val springBootVersion = "2.7.5"
9-
val springBoot3Version = "3.0.0"
9+
val springBoot3Version = "3.0.3"
1010
val kotlinCompatibleLanguageVersion = "1.4"
1111

1212
val composeVersion = "1.1.1"
@@ -107,7 +107,8 @@ object Config {
107107

108108
val fragment = "androidx.fragment:fragment-ktx:1.3.5"
109109

110-
val reactorCore = "io.projectreactor:reactor-core:3.4.6"
110+
val reactorCore = "io.projectreactor:reactor-core:3.5.3"
111+
val contextPropagation = "io.micrometer:context-propagation:1.0.2"
111112

112113
private val feignVersion = "11.6"
113114
val feignCore = "io.github.openfeign:feign-core:$feignVersion"

sentry-samples/sentry-samples-spring-boot-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/TodoController.java

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

3+
import io.sentry.spring.jakarta.webflux.ReactorUtils;
34
import org.springframework.web.bind.annotation.GetMapping;
45
import org.springframework.web.bind.annotation.PathVariable;
56
import org.springframework.web.bind.annotation.RestController;
67
import org.springframework.web.client.RestTemplate;
78
import org.springframework.web.reactive.function.client.WebClient;
9+
import reactor.core.publisher.Hooks;
10+
import reactor.core.publisher.Mono;
11+
import reactor.core.scheduler.Schedulers;
812

913
@RestController
1014
public class TodoController {
@@ -24,11 +28,18 @@ Todo todo(@PathVariable Long id) {
2428

2529
@GetMapping("/todo-webclient/{id}")
2630
Todo todoWebClient(@PathVariable Long id) {
27-
return webClient
28-
.get()
29-
.uri("https://jsonplaceholder.typicode.com/todos/{id}", id)
30-
.retrieve()
31-
.bodyToMono(Todo.class)
31+
Hooks.enableAutomaticContextPropagation();
32+
return ReactorUtils.withSentry(
33+
Mono.just(true)
34+
.publishOn(Schedulers.boundedElastic())
35+
.flatMap(
36+
x ->
37+
webClient
38+
.get()
39+
.uri("https://jsonplaceholder.typicode.com/todos/{id}", id)
40+
.retrieve()
41+
.bodyToMono(Todo.class)
42+
.map(response -> response)))
3243
.block();
3344
}
3445
}

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ repositories {
1919

2020
dependencies {
2121
implementation(Config.Libs.springBoot3StarterWebflux)
22+
implementation(Config.Libs.contextPropagation)
2223
implementation(Config.Libs.kotlinReflect)
2324
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
2425
implementation(projects.sentrySpringBootStarterJakarta)
2526
implementation(projects.sentryLogback)
27+
2628
testImplementation(Config.Libs.springBoot3StarterTest) {
2729
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
2830
}

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/Person.java renamed to sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/Person.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.sentry.samples.spring.boot;
1+
package io.sentry.samples.spring.boot.jakarta;
22

33
public class Person {
44
private final String firstName;

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/PersonController.java renamed to sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.sentry.samples.spring.boot;
1+
package io.sentry.samples.spring.boot.jakarta;
22

33
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/PersonService.java renamed to sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/PersonService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.sentry.samples.spring.boot;
1+
package io.sentry.samples.spring.boot.jakarta;
22

33
import io.sentry.Sentry;
44
import java.time.Duration;

sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/SentryDemoApplication.java renamed to sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/java/io/sentry/samples/spring/boot/jakarta/SentryDemoApplication.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
package io.sentry.samples.spring.boot;
1+
package io.sentry.samples.spring.boot.jakarta;
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.web.reactive.function.client.WebClient;
57

68
@SpringBootApplication
79
public class SentryDemoApplication {
810
public static void main(String[] args) {
911
SpringApplication.run(SentryDemoApplication.class, args);
1012
}
13+
14+
@Bean
15+
WebClient webClient(WebClient.Builder builder) {
16+
return builder.build();
17+
}
1118
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.sentry.samples.spring.boot.jakarta;
2+
3+
public class Todo {
4+
private final Long id;
5+
private final String title;
6+
private final boolean completed;
7+
8+
public Todo(Long id, String title, boolean completed) {
9+
this.id = id;
10+
this.title = title;
11+
this.completed = completed;
12+
}
13+
14+
public Long getId() {
15+
return id;
16+
}
17+
18+
public String getTitle() {
19+
return title;
20+
}
21+
22+
public boolean isCompleted() {
23+
return completed;
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.sentry.samples.spring.boot.jakarta;
2+
3+
import org.springframework.web.bind.annotation.GetMapping;
4+
import org.springframework.web.bind.annotation.PathVariable;
5+
import org.springframework.web.bind.annotation.RestController;
6+
import org.springframework.web.reactive.function.client.WebClient;
7+
import reactor.core.publisher.Mono;
8+
9+
@RestController
10+
public class TodoController {
11+
private final WebClient webClient;
12+
13+
public TodoController(WebClient webClient) {
14+
this.webClient = webClient;
15+
}
16+
17+
@GetMapping("/todo-webclient/{id}")
18+
Mono<Todo> todoWebClient(@PathVariable Long id) {
19+
return webClient
20+
.get()
21+
.uri("https://jsonplaceholder.typicode.com/todos/{id}", id)
22+
.retrieve()
23+
.bodyToMono(Todo.class)
24+
.map(response -> response);
25+
}
26+
}

0 commit comments

Comments
 (0)