diff --git a/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts index 74f95e193cf..338241078ae 100644 --- a/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts @@ -34,6 +34,7 @@ tasks.withType().configureEach { dependencies { implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentless) + implementation(projects.sentryAsyncProfiler) testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(projects.sentry) diff --git a/sentry-samples/sentry-samples-console-opentelemetry-noagent/src/test/kotlin/sentry/systemtest/ConsoleApplicationSystemTest.kt b/sentry-samples/sentry-samples-console-opentelemetry-noagent/src/test/kotlin/sentry/systemtest/ConsoleApplicationSystemTest.kt index 29d144355a7..8fc3909fa5c 100644 --- a/sentry-samples/sentry-samples-console-opentelemetry-noagent/src/test/kotlin/sentry/systemtest/ConsoleApplicationSystemTest.kt +++ b/sentry-samples/sentry-samples-console-opentelemetry-noagent/src/test/kotlin/sentry/systemtest/ConsoleApplicationSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import java.util.concurrent.TimeUnit import org.junit.Assert.assertEquals @@ -24,10 +25,12 @@ class ConsoleApplicationSystemTest { jarFile, mapOf( "SENTRY_DSN" to testHelper.dsn, - // "SENTRY_AUTO_INIT" to "false", + // "SENTRY_AUTO_INIT" to "true", "SENTRY_TRACES_SAMPLE_RATE" to "1.0", "SENTRY_ENABLE_PRETTY_SERIALIZATION_OUTPUT" to "false", "SENTRY_DEBUG" to "true", + "SENTRY_PROFILE_SESSION_SAMPLE_RATE" to "1.0", + "SENTRY_PROFILE_LIFECYCLE" to "TRACE", "OTEL_METRICS_EXPORTER" to "none", "OTEL_LOGS_EXPORTER" to "none", "OTEL_TRACES_EXPORTER" to "none", @@ -42,6 +45,8 @@ class ConsoleApplicationSystemTest { } private fun verifyExpectedEvents() { + var profilerId: SentryId? = null + // Verify we received a "Fatal message!" event testHelper.ensureErrorReceived { event -> event.message?.formatted == "Fatal message!" && event.level?.name == "FATAL" @@ -67,10 +72,15 @@ class ConsoleApplicationSystemTest { // Verify we received transaction events testHelper.ensureTransactionReceived { transaction, _ -> + profilerId = transaction.contexts.profile?.profilerId transaction.transaction == "transaction name" && transaction.spans?.any { span -> span.op == "child" } == true } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + // Verify we received the loop messages (should be 10 of them) var loopMessageCount = 0 try { diff --git a/sentry-samples/sentry-samples-console/build.gradle.kts b/sentry-samples/sentry-samples-console/build.gradle.kts index 3e4a2b889f3..5737e8effe0 100644 --- a/sentry-samples/sentry-samples-console/build.gradle.kts +++ b/sentry-samples/sentry-samples-console/build.gradle.kts @@ -34,6 +34,7 @@ tasks.withType().configureEach { dependencies { implementation(projects.sentry) + implementation(projects.sentryAsyncProfiler) testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(projects.sentry) diff --git a/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt b/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt index cf09728047d..ac4849f241a 100644 --- a/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt +++ b/sentry-samples/sentry-samples-console/src/test/kotlin/io/sentry/systemtest/ConsoleApplicationSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import java.util.concurrent.TimeUnit import org.junit.Assert.assertEquals @@ -27,6 +28,8 @@ class ConsoleApplicationSystemTest { "SENTRY_TRACES_SAMPLE_RATE" to "1.0", "SENTRY_ENABLE_PRETTY_SERIALIZATION_OUTPUT" to "false", "SENTRY_DEBUG" to "true", + "SENTRY_PROFILE_SESSION_SAMPLE_RATE" to "1.0", + "SENTRY_PROFILE_LIFECYCLE" to "TRACE", ), ) @@ -38,6 +41,7 @@ class ConsoleApplicationSystemTest { } private fun verifyExpectedEvents() { + var profilerId: SentryId? = null // Verify we received a "Fatal message!" event testHelper.ensureErrorReceived { event -> event.message?.formatted == "Fatal message!" && event.level?.name == "FATAL" @@ -63,10 +67,15 @@ class ConsoleApplicationSystemTest { // Verify we received transaction events testHelper.ensureTransactionReceived { transaction, _ -> + profilerId = transaction.contexts.profile?.profilerId transaction.transaction == "transaction name" && transaction.spans?.any { span -> span.op == "child" } == true } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + // Verify we received the loop messages (should be 10 of them) var loopMessageCount = 0 try { diff --git a/sentry-samples/sentry-samples-spring-7/build.gradle.kts b/sentry-samples/sentry-samples-spring-7/build.gradle.kts index 1d31ff91d01..a8f2dc4da7c 100644 --- a/sentry-samples/sentry-samples-spring-7/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-7/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) implementation(projects.sentrySpring7) implementation(projects.sentryLogback) + implementation(projects.sentryAsyncProfiler) implementation(libs.jackson.databind) implementation(libs.logback.classic) implementation(libs.servlet.jakarta.api) diff --git a/sentry-samples/sentry-samples-spring-7/src/main/resources/sentry.properties b/sentry-samples/sentry-samples-spring-7/src/main/resources/sentry.properties index 3cd4a7b9b08..b1cbcd91fa1 100644 --- a/sentry-samples/sentry-samples-spring-7/src/main/resources/sentry.properties +++ b/sentry-samples/sentry-samples-spring-7/src/main/resources/sentry.properties @@ -1,2 +1,5 @@ debug=true in-app-includes="io.sentry.samples" +profile-session-sample-rate=1.0 +profiling-traces-dir-path=tmp/sentry/profiling-traces +profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 3ad118d52f6..99968ef8f51 100644 --- a/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-7/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -48,4 +49,24 @@ class PersonSystemTest { assertEquals(person.firstName, returnedPerson!!.firstName) assertEquals(person.lastName, returnedPerson!!.lastName) } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts index fc3305f42c4..71ff985d67c 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentlessSpring) + implementation(projects.sentryAsyncProfiler) // database query tracing implementation(projects.sentryJdbc) diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/main/resources/application.properties index f9c648d6ff4..d19c33a3d1b 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/main/resources/application.properties @@ -16,7 +16,10 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index f277ff22025..50bc732b657 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -80,6 +81,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts index 86aacf5b658..ef38162d6bf 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) + implementation(projects.sentryAsyncProfiler) implementation(libs.otel) // database query tracing diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties index c1499111d31..6b57706019b 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/main/resources/application.properties @@ -16,7 +16,10 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 6a4a48cde3d..a4d7cc5bdc5 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -75,6 +76,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts index e9e0af50e70..b9986a31d02 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryJdbc) implementation(projects.sentryGraphql22) + implementation(projects.sentryAsyncProfiler) implementation(libs.context.propagation) implementation(libs.springboot4.starter.actuator) implementation(libs.springboot4.starter.graphql) diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties index e7944f4a962..9e9d6596e08 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/main/resources/application.properties @@ -12,3 +12,6 @@ sentry.traces-sample-rate=1.0 sentry.enable-backpressure-handling=true sentry.logs.enabled=true sentry.enable-spotlight=true +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index ac74d5e4953..7ba241200d4 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -52,4 +53,25 @@ class PersonSystemTest { testHelper.doesTransactionHaveOp(transaction, "http.server") } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + testHelper.doesTransactionHaveOp(transaction, "http.server") + } + + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts index ec91c09a3f2..a7fa57dac83 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) + implementation(projects.sentryAsyncProfiler) // database query tracing implementation(projects.sentryJdbc) diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties index 8e383764a43..9ba7a54aaf8 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties @@ -15,8 +15,11 @@ sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" sentry.logs.enabled=true +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index dde1eaa9938..362a8577148 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-4/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -72,4 +73,24 @@ class PersonSystemTest { ) } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts index 5a341f82892..b0fbae0ddc4 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts @@ -49,6 +49,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql22) implementation(projects.sentryQuartz) + implementation(projects.sentryAsyncProfiler) implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentlessSpring) // database query tracing diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/main/resources/application.properties index f9c648d6ff4..d19c33a3d1b 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/main/resources/application.properties @@ -16,7 +16,10 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index f277ff22025..50bc732b657 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -80,6 +81,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties index c1499111d31..6b57706019b 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/main/resources/application.properties @@ -16,7 +16,10 @@ sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.enablePrettySerializationOutput=false sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Uncomment and set to true to enable aot compatibility # This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan) diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 6a4a48cde3d..a4d7cc5bdc5 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -75,6 +76,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties index 2de573d81aa..9830709c313 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/src/main/resources/application.properties @@ -15,7 +15,7 @@ sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR sentry.enable-backpressure-handling=true sentry.enable-spotlight=false sentry.enablePrettySerializationOutput=false -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" sentry.logs.enabled=true sentry.profile-session-sample-rate=1.0 sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index dde1eaa9938..362a8577148 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -72,4 +73,24 @@ class PersonSystemTest { ) } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts index 2223458dc76..07e61c75af8 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { implementation(projects.sentryGraphql) implementation(projects.sentryQuartz) implementation(projects.sentryOpentelemetry.sentryOpentelemetryAgentlessSpring) + implementation(projects.sentryAsyncProfiler) // database query tracing implementation(projects.sentryJdbc) diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/main/resources/application.properties index 7b57c37b4cd..2225cd5045c 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/main/resources/application.properties @@ -15,7 +15,10 @@ sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Database configuration spring.datasource.url=jdbc:p6spy:hsqldb:mem:testdb diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index f277ff22025..50bc732b657 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -80,6 +81,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts index a704a6d6004..21a3cf3f7d5 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql) implementation(projects.sentryQuartz) + implementation(projects.sentryAsyncProfiler) implementation(libs.otel) // database query tracing diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/main/resources/application.properties index 2b00640f039..d39f38d7182 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/main/resources/application.properties @@ -15,7 +15,10 @@ sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Database configuration spring.datasource.url=jdbc:p6spy:hsqldb:mem:testdb diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 6a4a48cde3d..a4d7cc5bdc5 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -75,6 +76,26 @@ class PersonSystemTest { } } + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } + @Test fun `create person creates transaction if no sampled flag in sentry-trace header`() { val restClient = testHelper.restClient diff --git a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts index 7be7d68eb94..1cdff5cab38 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryJdbc) implementation(projects.sentryGraphql22) + implementation(projects.sentryAsyncProfiler) implementation(libs.context.propagation) implementation(libs.springboot3.starter.actuator) implementation(libs.springboot3.starter.graphql) diff --git a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/resources/application.properties index e7944f4a962..3bc4087b288 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/main/resources/application.properties @@ -12,3 +12,7 @@ sentry.traces-sample-rate=1.0 sentry.enable-backpressure-handling=true sentry.logs.enabled=true sentry.enable-spotlight=true +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index ac74d5e4953..a728fa7c314 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -52,4 +53,24 @@ class PersonSystemTest { testHelper.doesTransactionHaveOp(transaction, "http.server") } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + testHelper.doesTransactionHaveOp(transaction, "http.server") + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts index 58662e614d6..3c0a5f8c83e 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(projects.sentrySpringBootStarter) implementation(projects.sentryLogback) implementation(projects.sentryGraphql) + implementation(projects.sentryAsyncProfiler) testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(projects.sentrySystemTestSupport) diff --git a/sentry-samples/sentry-samples-spring-boot-webflux/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot-webflux/src/main/resources/application.properties index f899dde40b0..6544e24f13b 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot-webflux/src/main/resources/application.properties @@ -14,3 +14,7 @@ spring.graphql.schema.printer.enabled=true sentry.enable-backpressure-handling=true sentry.logs.enabled=true sentry.enable-spotlight=true +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring-boot-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index ac74d5e4953..a728fa7c314 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot-webflux/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -52,4 +53,24 @@ class PersonSystemTest { testHelper.doesTransactionHaveOp(transaction, "http.server") } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + testHelper.doesTransactionHaveOp(transaction, "http.server") + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts index 3958417edd4..be2b4583fb6 100644 --- a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(projects.sentryLogback) implementation(projects.sentryGraphql) implementation(projects.sentryQuartz) + implementation(projects.sentryAsyncProfiler) // database query tracing implementation(projects.sentryJdbc) diff --git a/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties b/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties index 2b00640f039..d39f38d7182 100644 --- a/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties +++ b/sentry-samples/sentry-samples-spring-boot/src/main/resources/application.properties @@ -15,7 +15,10 @@ sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR sentry.enable-backpressure-handling=true sentry.enable-spotlight=true sentry.logs.enabled=true -in-app-includes="io.sentry.samples" +sentry.in-app-includes="io.sentry.samples" +sentry.profile-session-sample-rate=1.0 +sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces +sentry.profile-lifecycle=TRACE # Database configuration spring.datasource.url=jdbc:p6spy:hsqldb:mem:testdb diff --git a/sentry-samples/sentry-samples-spring-boot/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-boot/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 73905c87409..0cae1acca40 100644 --- a/sentry-samples/sentry-samples-spring-boot/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-boot/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,6 +1,7 @@ package io.sentry.systemtest import io.sentry.protocol.FeatureFlag +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -62,4 +63,24 @@ class PersonSystemTest { ) } } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts index 591d49a84d4..8e450865659 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(kotlin(Config.kotlinStdLib)) implementation(projects.sentrySpringJakarta) implementation(projects.sentryLogback) + implementation(projects.sentryAsyncProfiler) implementation(libs.jackson.databind) implementation(libs.logback.classic) implementation(libs.servlet.jakarta.api) diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/main/resources/sentry.properties b/sentry-samples/sentry-samples-spring-jakarta/src/main/resources/sentry.properties index 3cd4a7b9b08..b1cbcd91fa1 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/main/resources/sentry.properties +++ b/sentry-samples/sentry-samples-spring-jakarta/src/main/resources/sentry.properties @@ -1,2 +1,5 @@ debug=true in-app-includes="io.sentry.samples" +profile-session-sample-rate=1.0 +profiling-traces-dir-path=tmp/sentry/profiling-traces +profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index 1215b819127..c806cf9b40e 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -48,4 +49,24 @@ class PersonSystemTest { assertEquals(person.firstName, returnedPerson!!.firstName) assertEquals(person.lastName, returnedPerson!!.lastName) } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-samples/sentry-samples-spring/build.gradle.kts b/sentry-samples/sentry-samples-spring/build.gradle.kts index cb114149452..f6aa0e925ee 100644 --- a/sentry-samples/sentry-samples-spring/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { implementation(kotlin(Config.kotlinStdLib)) implementation(projects.sentrySpring) implementation(projects.sentryLogback) + implementation(projects.sentryAsyncProfiler) implementation(libs.jackson.databind) implementation(libs.logback.classic) implementation(libs.servlet.api) diff --git a/sentry-samples/sentry-samples-spring/src/main/resources/sentry.properties b/sentry-samples/sentry-samples-spring/src/main/resources/sentry.properties index 3cd4a7b9b08..b1cbcd91fa1 100644 --- a/sentry-samples/sentry-samples-spring/src/main/resources/sentry.properties +++ b/sentry-samples/sentry-samples-spring/src/main/resources/sentry.properties @@ -1,2 +1,5 @@ debug=true in-app-includes="io.sentry.samples" +profile-session-sample-rate=1.0 +profiling-traces-dir-path=tmp/sentry/profiling-traces +profile-lifecycle=TRACE diff --git a/sentry-samples/sentry-samples-spring/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt b/sentry-samples/sentry-samples-spring/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt index aecc6ce24e1..2a1a60793ac 100644 --- a/sentry-samples/sentry-samples-spring/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt +++ b/sentry-samples/sentry-samples-spring/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt @@ -1,5 +1,6 @@ package io.sentry.systemtest +import io.sentry.protocol.SentryId import io.sentry.systemtest.util.TestHelper import kotlin.test.Test import kotlin.test.assertEquals @@ -48,4 +49,24 @@ class PersonSystemTest { assertEquals(person.firstName, returnedPerson!!.firstName) assertEquals(person.lastName, returnedPerson!!.lastName) } + + @Test + fun `create person starts a profile linked to the transaction`() { + var profilerId: SentryId? = null + val restClient = testHelper.restClient + val person = Person("firstA", "lastB") + val returnedPerson = restClient.createPerson(person) + assertEquals(200, restClient.lastKnownStatusCode) + + assertEquals(person.firstName, returnedPerson!!.firstName) + assertEquals(person.lastName, returnedPerson!!.lastName) + + testHelper.ensureTransactionReceived { transaction, envelopeHeader -> + profilerId = transaction.contexts.profile?.profilerId + transaction.transaction == "POST /person/" + } + testHelper.ensureProfileChunkReceived { profileChunk, envelopeHeader -> + profileChunk.profilerId == profilerId + } + } } diff --git a/sentry-system-test-support/api/sentry-system-test-support.api b/sentry-system-test-support/api/sentry-system-test-support.api index 1f5d1382a93..75ea52fc853 100644 --- a/sentry-system-test-support/api/sentry-system-test-support.api +++ b/sentry-system-test-support/api/sentry-system-test-support.api @@ -587,6 +587,7 @@ public final class io/sentry/systemtest/util/TestHelper { public final fun ensureNoEnvelopeReceived (Lkotlin/jvm/functions/Function1;)V public final fun ensureNoErrors (Lcom/apollographql/apollo3/api/ApolloResponse;)V public final fun ensureNoTransactionReceived (Lkotlin/jvm/functions/Function2;)V + public final fun ensureProfileChunkReceived (Lkotlin/jvm/functions/Function2;)V public final fun ensureTransactionReceived (Lkotlin/jvm/functions/Function2;)V public final fun ensureTransactionWithSpanReceived (Lkotlin/jvm/functions/Function1;)V public final fun findJar (Ljava/lang/String;Ljava/lang/String;)Ljava/io/File; diff --git a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt index 5a334ba2432..9a4a2c58dbe 100644 --- a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt +++ b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/TestHelper.kt @@ -3,6 +3,7 @@ package io.sentry.systemtest.util import com.apollographql.apollo3.api.ApolloResponse import com.apollographql.apollo3.api.Operation import io.sentry.JsonSerializer +import io.sentry.ProfileChunk import io.sentry.SentryEnvelopeHeader import io.sentry.SentryEvent import io.sentry.SentryItemType @@ -12,7 +13,10 @@ import io.sentry.protocol.FeatureFlag import io.sentry.protocol.SentrySpan import io.sentry.protocol.SentryTransaction import io.sentry.systemtest.graphql.GraphqlTestClient +import java.io.BufferedReader +import java.io.ByteArrayInputStream import java.io.File +import java.io.InputStreamReader import java.io.PrintWriter import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -82,6 +86,10 @@ class TestHelper(backendUrl: String) { ensureEnvelopeReceived { envelopeString -> checkIfTransactionMatches(envelopeString, callback) } } + fun ensureProfileChunkReceived(callback: ((ProfileChunk, SentryEnvelopeHeader) -> Boolean)) { + ensureEnvelopeReceived { envelopeString -> checkIfProfileMatches(envelopeString, callback) } + } + fun ensureNoTransactionReceived( callback: ((SentryTransaction, SentryEnvelopeHeader) -> Boolean) ) { @@ -154,6 +162,35 @@ class TestHelper(backendUrl: String) { return callback(transaction, envelopeHeader) } + private fun checkIfProfileMatches( + envelopeString: String, + callback: ((ProfileChunk, SentryEnvelopeHeader) -> Boolean), + ): Boolean { + val deserializeEnvelope = jsonSerializer.deserializeEnvelope(envelopeString.byteInputStream()) + if (deserializeEnvelope == null) { + return false + } + + val envelopeHeader = deserializeEnvelope.header + + val profileChunkItem = + deserializeEnvelope.items.firstOrNull { it.header.type == SentryItemType.ProfileChunk } + + if (profileChunkItem == null) { + return false + } + + val chunk = + BufferedReader(InputStreamReader(ByteArrayInputStream(profileChunkItem.data), Charsets.UTF_8)) + .use { eventReader -> jsonSerializer.deserialize(eventReader, ProfileChunk::class.java) } + + if (chunk == null) { + return false + } + + return callback(chunk, envelopeHeader) + } + fun ensureErrorReceived(callback: ((SentryEvent) -> Boolean)) { ensureEnvelopeReceived(retryCount = 3) { envelopeString -> val deserializeEnvelope = jsonSerializer.deserializeEnvelope(envelopeString.byteInputStream()) diff --git a/test/system-test-runner.py b/test/system-test-runner.py index 182bf164f42..1188e6efbc4 100644 --- a/test/system-test-runner.py +++ b/test/system-test-runner.py @@ -55,6 +55,9 @@ SENTRY_ENVIRONMENT_VARIABLES = { "SENTRY_DSN": "http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0", "SENTRY_TRACES_SAMPLE_RATE": "1.0", + "SENTRY_PROFILE_SESSION_SAMPLE_RATE": "1.0", + "SENTRY_PROFILE_LIFECYCLE": "TRACE", + "SENTRY_PROFILING_TRACES_DIR_PATH": "tmp/sentry/profiling-traces", "OTEL_TRACES_EXPORTER": "none", "OTEL_METRICS_EXPORTER": "none", "OTEL_LOGS_EXPORTER": "none",