Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/workflows/spring-boot-4-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ jobs:
- name: Build sample artifacts
run: |
./gradlew \
:sentry-samples:sentry-samples-spring-boot-4:bootJar \
:sentry-samples:sentry-samples-spring-boot-4:testClasses \
:sentry-samples:sentry-samples-spring-boot-4-webflux:bootJar \
:sentry-samples:sentry-samples-spring-boot-4-webflux:testClasses \
:sentry-samples:sentry-samples-spring-boot-4-opentelemetry:bootJar \
Expand All @@ -86,13 +84,6 @@ jobs:
- name: Test sentry-spring-boot-4 integration suite
run: ./gradlew :sentry-spring-boot-4:integrationTest

- name: Run sentry-samples-spring-boot-4
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot-4" \
--agent false \
--auto-init "true"

- name: Run sentry-samples-spring-boot-4-webflux
run: |
python3 test/system-test-runner.py test \
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/system-tests-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ jobs:
- sample: "sentry-samples-jul"
agent: "false"
agent-auto-init: "true"
- sample: "sentry-samples-spring-boot-4"
agent: "false"
agent-auto-init: "true"
- sample: "sentry-samples-spring-boot-4-log4j2"
agent: "false"
agent-auto-init: "true"
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ spring-kafka2 = { module = "org.springframework.kafka:spring-kafka", version = "
spring-kafka3 = { module = "org.springframework.kafka:spring-kafka", version.ref = "springKafka3" }
spring-kafka3-test = { module = "org.springframework.kafka:spring-kafka-test", version.ref = "springKafka3" }
spring-kafka4 = { module = "org.springframework.kafka:spring-kafka" }
spring-kafka4-test = { module = "org.springframework.kafka:spring-kafka-test" }
kafka-clients = { module = "org.apache.kafka:kafka-clients", version = "3.8.1" }
springboot4-otel = { module = "io.opentelemetry.instrumentation:opentelemetry-spring-boot-starter", version.ref = "otelInstrumentation" }
springboot4-resttestclient = { module = "org.springframework.boot:spring-boot-resttestclient", version.ref = "springboot4" }
Expand Down
32 changes: 0 additions & 32 deletions sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ plugins {
alias(libs.plugins.spring.dependency.management)
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
id("io.sentry.systemtest")
}

group = "io.sentry.sample.spring-boot-4"
Expand Down Expand Up @@ -71,35 +70,4 @@ dependencies {
// database query tracing
implementation(projects.sentryJdbc)
runtimeOnly(libs.hsqldb)

testImplementation(kotlin(Config.kotlinStdLib))
testImplementation(projects.sentry)
testImplementation(projects.sentrySystemTestSupport)
testImplementation(libs.apollo3.kotlin)
testImplementation(libs.kotlin.test.junit)
testImplementation(libs.slf4j2.api)
testImplementation(libs.springboot4.starter.test) {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation("ch.qos.logback:logback-classic:1.5.16")
testImplementation("ch.qos.logback:logback-core:1.5.16")
}

tasks.register<Test>("systemTest").configure {
group = "verification"
description = "Runs the System tests"

val test = project.extensions.getByType<SourceSetContainer>()["test"]
testClassesDirs = test.output.classesDirs
classpath = test.runtimeClasspath

maxParallelForks = 1

filter { includeTestsMatching("io.sentry.systemtest*") }
}

tasks.named("test").configure {
require(this is Test)

filter { excludeTestsMatching("io.sentry.systemtest.*") }
}

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 3 additions & 0 deletions sentry-spring-boot-4/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ testing {
implementation(projects.sentryAsyncProfiler)
implementation(projects.sentryGraphql22)
implementation(projects.sentryJdbc)
implementation(projects.sentryKafka)
implementation(projects.sentryLogback)
implementation(projects.sentryOkhttp)
implementation(projects.sentryTestSupport)
Expand All @@ -186,11 +187,13 @@ testing {
implementation(libs.springboot4.starter.cache)
implementation(libs.springboot4.starter.graphql)
implementation(libs.springboot4.starter.jdbc)
implementation(libs.springboot4.starter.kafka)
implementation(libs.springboot4.starter.restclient)
implementation(libs.springboot4.starter.test)
implementation(libs.springboot4.starter.web)
implementation(libs.springboot4.starter.webclient)
implementation(libs.springboot4.starter.webflux)
implementation(libs.spring.kafka4.test)
runtimeOnly(libs.hsqldb)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.boot.resttestclient.TestRestTemplate
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.kafka.test.context.EmbeddedKafka
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.springframework.test.context.junit4.SpringRunner

/**
* Drives [IntegrationTestApplication] over HTTP and asserts what reaches [server].
*
* Subclasses share one application context and one server, so the app starts once per suite.
* Envelopes that arrive late can outlive [server]'s reset, so tests match on values only they
* produce.
* Subclasses share one application context, one server and one Kafka broker, so each starts once
* per suite. Envelopes that arrive late can outlive [server]'s reset, so tests match on values only
* they produce.
*/
@RunWith(SpringRunner::class)
// An in-JVM KRaft broker, so no Docker is needed. As part of the context, it starts before the
// app's Kafka listener connects and stops after it.
@EmbeddedKafka(topics = [KAFKA_TOPIC], bootstrapServersProperty = "spring.kafka.bootstrap-servers")
@SpringBootTest(
classes = [IntegrationTestApplication::class],
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
Expand All @@ -27,12 +31,14 @@ import org.springframework.test.context.junit4.SpringRunner
"sentry.traces-sample-rate=1.0",
"sentry.clients.ok-http-enabled=true",
"sentry.enable-cache-tracing=true",
"sentry.enable-queue-tracing=true",
"sentry.logs.enabled=true",
"sentry.profile-session-sample-rate=1.0",
"sentry.profile-lifecycle=TRACE",
"sentry.profiling-traces-dir-path=\${java.io.tmpdir}/sentry-integration-test/profiling-traces",
"spring.datasource.url=jdbc:p6spy:hsqldb:mem:testdb",
"spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver",
"spring.kafka.consumer.auto-offset-reset=earliest",
],
)
abstract class IntegrationTest {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.sentry.spring.boot4.integrationtest

import org.springframework.kafka.annotation.KafkaListener
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

const val KAFKA_TOPIC = "sentry-topic"

@RestController
@RequestMapping("/kafka")
class KafkaController(private val kafkaTemplate: KafkaTemplate<String, String>) {
@GetMapping("/produce")
fun produce(@RequestParam message: String): String {
kafkaTemplate.send(KAFKA_TOPIC, message)
return "Message sent: $message"
}
}

@Component
class KafkaConsumer {
/** Does nothing with the message; Sentry traces the listener invocation. */
@KafkaListener(topics = [KAFKA_TOPIC], groupId = "sentry-integration-test")
fun listen(message: String) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.sentry.spring.boot4.integrationtest

import com.google.common.truth.Truth.assertThat
import io.sentry.protocol.SentryTransaction
import io.sentry.test.transaction
import kotlin.test.Test

/** A message sent through Kafka is traced on both sides: where it's published and processed. */
class KafkaIntegrationTest : IntegrationTest() {

@Test
fun `publishing a message traces a queue publish span`() {
val producer = produce("publish-test")

val span = producer.spans.single { it.op == "queue.publish" }
assertThat(span.data).containsEntry("messaging.system", "kafka")
assertThat(span.data).containsEntry("messaging.destination.name", KAFKA_TOPIC)
}

@Test
fun `processing a message continues the producer's trace`() {
val traceId = produce("process-test").contexts.trace!!.traceId

val consumer =
server
.awaitEnvelope {
it.transaction()?.contexts?.trace?.let { trace ->
trace.operation == "queue.process" && trace.traceId == traceId
} == true
}
.transaction()!!
val data = consumer.contexts.trace!!.data
assertThat(data).containsEntry("messaging.system", "kafka")
assertThat(data).containsEntry("messaging.destination.name", KAFKA_TOPIC)
}

/** Sends [message] through the app and returns the transaction of the request that sent it. */
private fun produce(message: String): SentryTransaction {
assertThat(restTemplate.getForObject("/kafka/produce?message=$message", String::class.java))
.isEqualTo("Message sent: $message")
return server
.awaitEnvelope {
it.transaction()?.let { t ->
t.transaction == "GET /kafka/produce" && t.request?.queryString == "message=$message"
} == true
}
.transaction()!!
}
}
3 changes: 0 additions & 3 deletions test/system-test-runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
"sentry-samples-spring-boot-jakarta-log4j2",
"sentry-samples-spring-boot-jakarta-opentelemetry",
"sentry-samples-spring-boot-jakarta-opentelemetry-noagent",
"sentry-samples-spring-boot-4",
"sentry-samples-spring-boot-4-log4j2",
"sentry-samples-spring-boot-4-opentelemetry",
"sentry-samples-spring-boot-4-opentelemetry-noagent",
Expand All @@ -88,7 +87,6 @@
"sentry-samples-spring-boot-jakarta-log4j2",
"sentry-samples-spring-boot-jakarta-opentelemetry",
"sentry-samples-spring-boot-jakarta-opentelemetry-noagent",
"sentry-samples-spring-boot-4",
"sentry-samples-spring-boot-4-log4j2",
"sentry-samples-spring-boot-4-opentelemetry",
"sentry-samples-spring-boot-4-opentelemetry-noagent",
Expand Down Expand Up @@ -862,7 +860,6 @@ def get_available_modules(self) -> List[ModuleConfig]:
ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "true", "false"),
ModuleConfig("sentry-samples-spring-boot-jakarta-opentelemetry", "true", "false", "false"),
ModuleConfig("sentry-samples-spring-boot-4-webflux", "false", "true", "false"),
ModuleConfig("sentry-samples-spring-boot-4", "false", "true", "false"),
ModuleConfig("sentry-samples-spring-boot-4-log4j2", "false", "true", "false"),
ModuleConfig("sentry-samples-spring-boot-4-opentelemetry-noagent", "false", "true", "false"),
ModuleConfig("sentry-samples-spring-boot-4-opentelemetry", "true", "true", "false"),
Expand Down
Loading