Skip to content
Merged
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
1 change: 1 addition & 0 deletions .claude/skills/test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Extract the module name and optional test class filter from the argument.
| Signal | Test Type |
|--------|-----------|
| Path contains `/src/integrationTest*/` or `/src/agentFixture/` | Integration test suite |
| Module name only, and the user asks for integration or end-to-end tests | Integration test suite: each `src/integrationTest*/` directory of the module |
| Path contains `sentry-samples/`, or module name starts with `sentry-samples-` | None: samples have no tests |
| Everything else | Unit test |

Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ distributions/
*.vscode/
sentry-spring-boot-starter-jakarta/src/main/resources/META-INF/spring.factories
sentry-samples/sentry-samples-spring-boot-jakarta/spy.log
sentry-mock-server.txt
tomcat-server.txt
spring-server.txt
*.pid
spy.log
.kotlin
Expand Down
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ graphql-java22 = { module = "com.graphql-java:graphql-java", version = "22.1" }
graphql-java24 = { module = "com.graphql-java:graphql-java", version = "24.0" }
jackson-bom = { module = "com.fasterxml.jackson:jackson-bom", version.ref = "jackson" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
jetbrains-annotations = { module = "org.jetbrains:annotations", version = "23.0.0" }
kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.common.truth.Truth.assertThat
import io.sentry.opentelemetry.agent.fixture.HelloServer
import io.sentry.test.ChildJvmApp
import io.sentry.test.MockSentryServer
import io.sentry.test.requiredTestProperty
import io.sentry.test.transaction
import java.io.File
import java.net.URL
Expand Down Expand Up @@ -32,7 +33,7 @@ class AgentSmokeTest {
mainClass = HelloServer::class.java.name,
classpath =
File(HelloServer::class.java.protectionDomain.codeSource.location.toURI()).path,
jvmArgs = listOf("-javaagent:${System.getProperty("sentry.test.agentJar")}"),
jvmArgs = listOf("-javaagent:${requiredTestProperty("sentry.test.agentJar")}"),
env =
mapOf(
"SENTRY_DSN" to server.dsn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class AgentlessEndToEndTest {
}

@Test
fun `Sentry set up by OpenTelemetry reports events, a profiled transaction and metrics`() {
fun `Sentry set up by OpenTelemetry reports events, a profiled transaction, OpenTelemetry spans and metrics`() {
// The customizer's Sentry.init reads these as external configuration.
System.setProperty("sentry.dsn", server.dsn)
System.setProperty("sentry.traces-sample-rate", "1.0")
Expand Down Expand Up @@ -107,6 +107,11 @@ class AgentlessEndToEndTest {
Thread.sleep(100)
span.finish()
transaction.finish()
// A span started through the OpenTelemetry API only reaches Sentry if the customizer wired
// Sentry's span processor into the SDK.
val otelSpan =
GlobalOpenTelemetry.getTracer("agentless-test").spanBuilder("otel span").startSpan()
otelSpan.end()
Sentry.flush(5000)
// Sentry exports OpenTelemetry spans in batches every 5 seconds.
openTelemetry.sdkTracerProvider.forceFlush().join(5, TimeUnit.SECONDS)
Expand Down Expand Up @@ -138,6 +143,10 @@ class AgentlessEndToEndTest {
val sent =
server.awaitEnvelope { it.transaction()?.transaction == "transaction name" }.transaction()!!
assertThat(sent.spans.map { it.op }).contains("child")
val otelTransaction =
server.awaitEnvelope { it.transaction()?.transaction == "otel span" }.transaction()!!
assertThat(otelTransaction.contexts.trace!!.traceId.toString())
.isEqualTo(otelSpan.spanContext.traceId)
val profilerId = sent.contexts.profile?.profilerId
assertThat(profilerId).isNotNull()
server.awaitEnvelope(timeoutMillis = 30_000) { it.profileChunk()?.profilerId == profilerId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ group = "io.sentry.sample.spring-boot"

version = "0.0.1-SNAPSHOT"

fun springBoot2SupportsOptionalIntegrations(): Boolean {
val version = libs.versions.springboot2.get().removeSuffix(".RELEASE")
val parts = version.split(".").map { it.toIntOrNull() ?: 0 }
val major = parts.getOrElse(0) { 0 }
val minor = parts.getOrElse(1) { 0 }
return major > 2 || (major == 2 && minor >= 7)
}

val includeGraphql =
!project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations()
val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations()

tasks.withType<KotlinCompile>().configureEach {
kotlin {
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
Expand All @@ -49,9 +37,7 @@ dependencies {
implementation(libs.springboot.starter)
implementation(libs.springboot.starter.actuator)
implementation(libs.springboot.starter.aop)
if (includeGraphql) {
implementation(libs.springboot.starter.graphql)
}
implementation(libs.springboot.starter.graphql)
implementation(libs.springboot.starter.jdbc)
implementation(libs.springboot.starter.quartz)
implementation(libs.springboot.starter.security)
Expand All @@ -63,17 +49,13 @@ dependencies {
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
implementation(projects.sentrySpringBootStarter)
implementation(projects.sentryLogback)
if (includeGraphql) {
implementation(projects.sentryGraphql)
}
implementation(projects.sentryGraphql)
implementation(projects.sentryQuartz)
implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentlessSpring)
implementation(projects.sentryAsyncProfiler)

if (includeKafka) {
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)
}
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down Expand Up @@ -101,16 +83,3 @@ tasks.jar {
}

tasks.startScripts { dependsOn(tasks.shadowJar) }

configure<SourceSetContainer> {
main {
if (!includeGraphql) {
java.exclude("**/graphql/**")
resources.exclude("graphql/**")
}
if (!includeKafka) {
java.exclude("**/queues/kafka/**")
resources.exclude("application-kafka.properties")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ group = "io.sentry.sample.spring-boot"

version = "0.0.1-SNAPSHOT"

fun springBoot2SupportsOptionalIntegrations(): Boolean {
val version = libs.versions.springboot2.get().removeSuffix(".RELEASE")
val parts = version.split(".").map { it.toIntOrNull() ?: 0 }
val major = parts.getOrElse(0) { 0 }
val minor = parts.getOrElse(1) { 0 }
return major > 2 || (major == 2 && minor >= 7)
}

val includeGraphql =
!project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations()
val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations()

tasks.withType<KotlinCompile>().configureEach {
kotlin {
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
Expand All @@ -48,9 +36,7 @@ dependencies {
implementation(libs.springboot.starter)
implementation(libs.springboot.starter.actuator)
implementation(libs.springboot.starter.aop)
if (includeGraphql) {
implementation(libs.springboot.starter.graphql)
}
implementation(libs.springboot.starter.graphql)
implementation(libs.springboot.starter.jdbc)
implementation(libs.springboot.starter.quartz)
implementation(libs.springboot.starter.security)
Expand All @@ -62,17 +48,13 @@ dependencies {
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
implementation(projects.sentrySpringBootStarter)
implementation(projects.sentryLogback)
if (includeGraphql) {
implementation(projects.sentryGraphql)
}
implementation(projects.sentryGraphql)
implementation(projects.sentryQuartz)
implementation(projects.sentryAsyncProfiler)
implementation(libs.otel)

if (includeKafka) {
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)
}
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)

// database query tracing
implementation(projects.sentryJdbc)
Expand Down Expand Up @@ -101,19 +83,6 @@ tasks.jar {

tasks.startScripts { dependsOn(tasks.shadowJar) }

configure<SourceSetContainer> {
main {
if (!includeGraphql) {
java.exclude("**/graphql/**")
resources.exclude("graphql/**")
}
if (!includeKafka) {
java.exclude("**/queues/kafka/**")
resources.exclude("application-kafka.properties")
}
}
}

tasks.register<JavaExec>("bootRunWithAgent").configure {
group = "application"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,16 @@ group = "io.sentry.sample.spring-boot"

version = "0.0.1-SNAPSHOT"

fun springBoot2SupportsGraphql(): Boolean {
val version = libs.versions.springboot2.get().removeSuffix(".RELEASE")
val parts = version.split(".").map { it.toIntOrNull() ?: 0 }
val major = parts.getOrElse(0) { 0 }
val minor = parts.getOrElse(1) { 0 }
return major > 2 || (major == 2 && minor >= 7)
}

val includeGraphql = !project.hasProperty("excludeGraphql") && springBoot2SupportsGraphql()

dependencies {
implementation(platform(libs.springboot2.bom))
implementation(libs.springboot.starter.actuator)
if (includeGraphql) {
implementation(libs.springboot.starter.graphql)
}
implementation(libs.springboot.starter.graphql)
implementation(libs.springboot.starter.webflux)
implementation(Config.Libs.kotlinReflect)
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
implementation(projects.sentrySpringBootStarter)
implementation(projects.sentryLogback)
if (includeGraphql) {
implementation(projects.sentryGraphql)
}
implementation(projects.sentryGraphql)
implementation(projects.sentryAsyncProfiler)
}

Expand All @@ -74,15 +60,6 @@ tasks.jar {

tasks.startScripts { dependsOn(tasks.shadowJar) }

configure<SourceSetContainer> {
main {
if (!includeGraphql) {
java.exclude("**/graphql/**")
resources.exclude("graphql/**")
}
}
}

tasks.withType<KotlinCompile>().configureEach {
kotlin {
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
Expand Down
39 changes: 4 additions & 35 deletions sentry-samples/sentry-samples-spring-boot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ group = "io.sentry.sample.spring-boot"

version = "0.0.1-SNAPSHOT"

fun springBoot2SupportsOptionalIntegrations(): Boolean {
val version = libs.versions.springboot2.get().removeSuffix(".RELEASE")
val parts = version.split(".").map { it.toIntOrNull() ?: 0 }
val major = parts.getOrElse(0) { 0 }
val minor = parts.getOrElse(1) { 0 }
return major > 2 || (major == 2 && minor >= 7)
}

val includeGraphql =
!project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations()
val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations()

tasks.withType<KotlinCompile>().configureEach {
kotlin {
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
Expand All @@ -48,9 +36,7 @@ dependencies {
implementation(libs.springboot.starter)
implementation(libs.springboot.starter.actuator)
implementation(libs.springboot.starter.aop)
if (includeGraphql) {
implementation(libs.springboot.starter.graphql)
}
implementation(libs.springboot.starter.graphql)
implementation(libs.springboot.starter.jdbc)
implementation(libs.springboot.starter.quartz)
implementation(libs.springboot.starter.security)
Expand All @@ -60,18 +46,14 @@ dependencies {
implementation(libs.springboot.starter.websocket)
implementation(libs.caffeine)

if (includeKafka) {
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)
}
implementation(libs.spring.kafka2)
implementation(projects.sentryKafka)
implementation(Config.Libs.aspectj)
implementation(Config.Libs.kotlinReflect)
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
implementation(projects.sentrySpringBootStarter)
implementation(projects.sentryLogback)
if (includeGraphql) {
implementation(projects.sentryGraphql)
}
implementation(projects.sentryGraphql)
implementation(projects.sentryQuartz)
implementation(projects.sentryAsyncProfiler)

Expand Down Expand Up @@ -108,16 +90,3 @@ tasks.jar {
}

tasks.startScripts { dependsOn(tasks.shadowJar) }

configure<SourceSetContainer> {
main {
if (!includeGraphql) {
java.exclude("**/graphql/**")
resources.exclude("graphql/**")
}
if (!includeKafka) {
java.exclude("**/queues/kafka/**")
resources.exclude("application-kafka.properties")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,32 @@ class PersonIntegrationTest : IntegrationTest() {

@Test
fun `failed request reports errors, the transaction and logs`() {
val response = restTemplate.getForEntity("/person/1", String::class.java)
val response = restTemplate.getForEntity("/person/11", String::class.java)
assertThat(response.statusCode.value()).isEqualTo(500)
Sentry.flush(5000)

val logged =
server.awaitEnvelope { it.event()?.message?.formatted == "Trying person with id=1" }.event()!!
server
.awaitEnvelope { it.event()?.message?.formatted == "Trying person with id=11" }
.event()!!
assertThat(logged.hasFlag("my-feature-flag", true)).isTrue()

val thrown =
server
.awaitEnvelope {
it.event()?.exceptions?.any { e -> e.value == "Something went wrong [id=1]" } == true
it.event()?.exceptions?.any { e -> e.value == "Something went wrong [id=11]" } == true
}
.event()!!
assertThat(thrown.hasFlag("my-feature-flag", true)).isTrue()

val transaction =
server.awaitEnvelope { it.transaction()?.transaction == "GET /person/{id}" }.transaction()!!
server
.awaitEnvelope {
it.transaction()?.let { t ->
t.transaction == "GET /person/{id}" && t.request?.url?.endsWith("/person/11") == true
} == true
}
.transaction()!!
assertThat(
transaction.hasOp(
"http.server",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.sentry.spring.boot4.otelagentintegrationtest

import io.sentry.test.ChildJvmApp
import io.sentry.test.MockSentryServer
import io.sentry.test.requiredTestProperty
import org.springframework.boot.restclient.RestTemplateBuilder
import org.springframework.boot.resttestclient.TestRestTemplate
import org.springframework.kafka.test.EmbeddedKafkaKraftBroker
Expand All @@ -15,7 +16,7 @@ import org.springframework.kafka.test.EmbeddedKafkaKraftBroker
* Spring Boot starter.
*/
object AgentApp {
val autoInit = System.getProperty("sentry.test.agentAutoInit").toBoolean()
val autoInit = requiredTestProperty("sentry.test.agentAutoInit").toBoolean()

val server = MockSentryServer()

Expand All @@ -25,8 +26,8 @@ object AgentApp {
private val app =
ChildJvmApp.start(
mainClass = "io.sentry.spring.boot4.agentfixture.AgentFixtureApplicationKt",
classpath = System.getProperty("sentry.test.appClasspath"),
jvmArgs = listOf("-javaagent:${System.getProperty("sentry.test.agentJar")}"),
classpath = requiredTestProperty("sentry.test.appClasspath"),
jvmArgs = listOf("-javaagent:${requiredTestProperty("sentry.test.agentJar")}"),
// Environment variables, because the agent doesn't read Spring Boot's properties.
env =
mapOf(
Expand Down
Loading
Loading