Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ReactorUtils into its own sentry-reactor module #4155

Merged
merged 14 commits into from
Feb 17, 2025
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ object Config {
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
val SENTRY_COMPOSE_HELPER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.compose.helper"
val SENTRY_OKHTTP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.okhttp"
val SENTRY_REACTOR_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.reactor"
val group = "io.sentry"
val description = "SDK for sentry.io"
val versionNameProp = "versionName"
Expand Down
26 changes: 26 additions & 0 deletions sentry-reactor/api/sentry-reactor.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
public final class io/sentry/reactor/BuildConfig {
public static final field SENTRY_REACTOR_SDK_NAME Ljava/lang/String;
public static final field VERSION_NAME Ljava/lang/String;
}

public final class io/sentry/reactor/SentryReactorThreadLocalAccessor : io/micrometer/context/ThreadLocalAccessor {
public static final field KEY Ljava/lang/String;
public fun <init> ()V
public fun getValue ()Lio/sentry/IScopes;
public synthetic fun getValue ()Ljava/lang/Object;
public fun key ()Ljava/lang/Object;
public fun reset ()V
public fun setValue (Lio/sentry/IScopes;)V
public synthetic fun setValue (Ljava/lang/Object;)V
}

public final class io/sentry/reactor/SentryReactorUtils {
public fun <init> ()V
public static fun withSentry (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux;
public static fun withSentry (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono;
public static fun withSentryForkedRoots (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux;
public static fun withSentryForkedRoots (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono;
public static fun withSentryScopes (Lreactor/core/publisher/Flux;Lio/sentry/IScopes;)Lreactor/core/publisher/Flux;
public static fun withSentryScopes (Lreactor/core/publisher/Mono;Lio/sentry/IScopes;)Lreactor/core/publisher/Mono;
}

94 changes: 94 additions & 0 deletions sentry-reactor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import net.ltgt.gradle.errorprone.errorprone
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`java-library`
kotlin("jvm")
jacoco
id(Config.QualityPlugins.errorProne)
id(Config.QualityPlugins.gradleVersions)
id(Config.BuildPlugins.buildConfig) version Config.BuildPlugins.buildConfigVersion
}

configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
kotlinOptions.languageVersion = Config.kotlinCompatibleLanguageVersion
}

dependencies {
api(projects.sentry)
compileOnly(Config.Libs.reactorCore)
compileOnly(Config.Libs.contextPropagation)

compileOnly(Config.CompileOnly.nopen)
errorprone(Config.CompileOnly.nopenChecker)
errorprone(Config.CompileOnly.errorprone)
errorprone(Config.CompileOnly.errorProneNullAway)
compileOnly(Config.CompileOnly.jetbrainsAnnotations)

// tests
testImplementation(projects.sentryTestSupport)
testImplementation(kotlin(Config.kotlinStdLib))
testImplementation(Config.TestLibs.kotlinTestJunit)
testImplementation(Config.TestLibs.mockitoKotlin)

testImplementation(Config.Libs.reactorCore)
testImplementation(Config.Libs.contextPropagation)

testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
}

configure<SourceSetContainer> {
test {
java.srcDir("src/test/java")
}
}

jacoco {
toolVersion = Config.QualityPlugins.Jacoco.version
}

tasks.jacocoTestReport {
reports {
xml.required.set(true)
html.required.set(false)
}
}

tasks {
jacocoTestCoverageVerification {
violationRules {
rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } }
}
}
check {
dependsOn(jacocoTestCoverageVerification)
dependsOn(jacocoTestReport)
}
}

buildConfig {
useJavaOutput()
packageName("io.sentry.reactor")
buildConfigField("String", "SENTRY_REACTOR_SDK_NAME", "\"${Config.Sentry.SENTRY_REACTOR_SDK_NAME}\"")
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
}

val generateBuildConfig by tasks
tasks.withType<JavaCompile>().configureEach {
dependsOn(generateBuildConfig)
options.errorprone {
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", "io.sentry")
}
}

repositories {
mavenCentral()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.sentry.spring.jakarta.webflux;
package io.sentry.reactor;

import io.micrometer.context.ThreadLocalAccessor;
import io.sentry.IScopes;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.sentry.spring.jakarta.webflux;
package io.sentry.reactor;

import io.sentry.IScopes;
import io.sentry.Sentry;
Expand All @@ -9,7 +9,7 @@
import reactor.util.context.Context;

@ApiStatus.Experimental
public final class ReactorUtils {
public final class SentryReactorUtils {

/**
* Writes the current Sentry {@link IScopes} to the {@link Context} and uses {@link
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.sentry.reactor

import io.micrometer.context.ContextRegistry
import io.sentry.IScopes
import io.sentry.NoOpScopes
import io.sentry.Sentry
import org.mockito.kotlin.mock
import reactor.core.publisher.Flux
import reactor.core.publisher.Hooks
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotSame
// import org.mockito.kotlin.any
// import org.mockito.kotlin.verify
// import org.mockito.kotlin.whenever
// import kotlin.test.assertSame

class SentryReactorUtilsTest {

@BeforeTest
fun setup() {
println("setUp")
ContextRegistry.getInstance().registerThreadLocalAccessor(SentryReactorThreadLocalAccessor())
Hooks.enableAutomaticContextPropagation()
}

@AfterTest
fun teardown() {
println("teardown")
Sentry.setCurrentScopes(NoOpScopes.getInstance())
}

// @Test
// fun `propagates scopes inside mono`() {
// val scopesToUse = mock<IScopes>()
// var scopesInside: IScopes? = null
// val mono = SentryReactorUtils.withSentryScopes(
// Mono.just("hello")
// .publishOn(Schedulers.boundedElastic())
// .map { it ->
// scopesInside = Sentry.getCurrentScopes()
// it
// },
// scopesToUse
// )

// assertEquals("hello", mono.block())
// assertSame(scopesToUse, scopesInside)
// }

// @Test
// fun `propagates scopes inside flux`() {
// val scopesToUse = mock<IScopes>()
// var scopesInside: IScopes? = null
// val flux = SentryReactorUtils.withSentryScopes(
// Flux.just("hello")
// .publishOn(Schedulers.boundedElastic())
// .map { it ->
// scopesInside = Sentry.getCurrentScopes()
// it
// },
// scopesToUse
// )

// assertEquals("hello", flux.blockFirst())
// assertSame(scopesToUse, scopesInside)
// }

@Test
fun `without reactive utils scopes is not propagated to mono`() {
val scopesToUse = mock<IScopes>()
var scopesInside: IScopes? = null
val mono = Mono.just("hello")
.publishOn(Schedulers.boundedElastic())
.map { it ->
scopesInside = Sentry.getCurrentScopes()
it
}

assertEquals("hello", mono.block())
assertNotSame(scopesToUse, scopesInside)
}

@Test
fun `without reactive utils scopes is not propagated to flux`() {
val scopesToUse = mock<IScopes>()
var scopesInside: IScopes? = null
val flux = Flux.just("hello")
.publishOn(Schedulers.boundedElastic())
.map { it ->
scopesInside = Sentry.getCurrentScopes()
it
}

assertEquals("hello", flux.blockFirst())
assertNotSame(scopesToUse, scopesInside)
}

// @Test
// fun `clones scopes for mono`() {
// val mockScopes = mock<IScopes>()
// whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock<IScopes>())
// Sentry.setCurrentScopes(mockScopes)
// SentryReactorUtils.withSentry(Mono.just("hello")).block()

// verify(mockScopes).forkedCurrentScope(any())
// }

// @Test
// fun `clones scopes for flux`() {
// val mockScopes = mock<IScopes>()
// whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock<IScopes>())
// Sentry.setCurrentScopes(mockScopes)
// SentryReactorUtils.withSentry(Flux.just("hello")).blockFirst()

// verify(mockScopes).forkedCurrentScope(any())
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dependencies {
implementation(projects.sentryGraphql22)
implementation(projects.sentryQuartz)
implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentlessSpring)
implementation(projects.sentryReactor)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.opentelemetry.context.Scope;
import io.sentry.ISpan;
import io.sentry.Sentry;
import io.sentry.spring.jakarta.webflux.ReactorUtils;
import io.sentry.reactor.SentryReactorUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand Down Expand Up @@ -51,7 +51,7 @@ Todo todo(@PathVariable Long id) {
@GetMapping("/todo-webclient/{id}")
Todo todoWebClient(@PathVariable Long id) {
Hooks.enableAutomaticContextPropagation();
return ReactorUtils.withSentry(
return SentryReactorUtils.withSentry(
Mono.just(true)
.publishOn(Schedulers.boundedElastic())
.flatMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ dependencies {
implementation(projects.sentryGraphql22)
implementation(projects.sentryQuartz)
implementation(Config.Libs.OpenTelemetry.otelSdk)
implementation(projects.sentryReactor)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import io.opentelemetry.context.Scope;
import io.sentry.ISpan;
import io.sentry.Sentry;
import io.sentry.spring.jakarta.webflux.ReactorUtils;
import io.sentry.reactor.SentryReactorUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand Down Expand Up @@ -51,7 +51,7 @@ Todo todo(@PathVariable Long id) {
@GetMapping("/todo-webclient/{id}")
Todo todoWebClient(@PathVariable Long id) {
Hooks.enableAutomaticContextPropagation();
return ReactorUtils.withSentry(
return SentryReactorUtils.withSentry(
Mono.just(true)
.publishOn(Schedulers.boundedElastic())
.flatMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dependencies {
implementation(projects.sentryLogback)
implementation(projects.sentryGraphql22)
implementation(projects.sentryQuartz)
implementation(projects.sentryReactor)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.sentry.samples.spring.boot.jakarta;

import io.sentry.spring.jakarta.webflux.ReactorUtils;
import io.sentry.reactor.SentryReactorUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -32,7 +32,7 @@ Todo todo(@PathVariable Long id) {
@GetMapping("/todo-webclient/{id}")
Todo todoWebClient(@PathVariable Long id) {
Hooks.enableAutomaticContextPropagation();
return ReactorUtils.withSentry(
return SentryReactorUtils.withSentry(
Mono.just(true)
.publishOn(Schedulers.boundedElastic())
.flatMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
implementation(projects.sentryLogback)
implementation(projects.sentryJdbc)
implementation(projects.sentryGraphql22)
implementation(projects.sentryReactor)

testImplementation(Config.Libs.springBoot3StarterTest) {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
Expand Down
2 changes: 2 additions & 0 deletions sentry-spring-boot-jakarta/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
compileOnly(Config.Libs.OpenTelemetry.otelSdk)
compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryCore)
compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization)
compileOnly(projects.sentryReactor)

annotationProcessor(platform(SpringBootPlugin.BOM_COORDINATES))
annotationProcessor(Config.AnnotationProcessors.springBootAutoConfigure)
Expand Down Expand Up @@ -85,6 +86,7 @@ dependencies {
testImplementation(projects.sentryOpentelemetry.sentryOpentelemetryAgent)
testImplementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization)
testImplementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap)
testImplementation(projects.sentryReactor)
}

configure<SourceSetContainer> {
Expand Down
Loading
Loading