From bf3610d9b1bd0fe798873b9d3d717d3ff769efd3 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 27 Oct 2026 11:42:15 +0100 Subject: [PATCH 1/2] feat(opentelemetry): Honor OTEL_BSP_SCHEDULE_DELAY in the agent The agent built Sentry's BatchSpanProcessor with the OpenTelemetry default, so spans always reached Sentry in 5 second batches, whatever otel.bsp.schedule.delay said. Read it from the agent's config instead. The Boot 4 agent suites set it to 100ms. Each of their tests waited for a 5 second batch, which made them take minutes on CI. Co-Authored-By: Claude Opus 5.5 --- .cursor/rules/e2e_tests.mdc | 6 ++++-- .../SentryAutoConfigurationCustomizerProvider.java | 13 ++++++++++++- .../boot4/otelagentintegrationtest/AgentApp.kt | 2 ++ .../DistributedTracingIntegrationTest.kt | 4 ++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.cursor/rules/e2e_tests.mdc b/.cursor/rules/e2e_tests.mdc index 1785e61b5c1..4063c007838 100644 --- a/.cursor/rules/e2e_tests.mdc +++ b/.cursor/rules/e2e_tests.mdc @@ -74,8 +74,10 @@ it. Gradle runs the two in parallel. customizes, and that the OpenTelemetry starter instruments, so it adds or replaces trace headers on the requests a test sends. Build one outside the context. - Sentry exports OpenTelemetry spans in batches every 5 seconds. In-process, flush the tracer - provider from the server's hook. With the agent this isn't possible, so each test waits for a - batch and negative checks wait at least 6 seconds. + provider from the server's hook. With the agent this isn't possible, so set + `OTEL_BSP_SCHEDULE_DELAY` on the app to shorten the batch delay, and make negative checks wait + well past it. The Boot 2 and Boot 3 agent suites don't set it yet: their tests wait 5 seconds + for a batch and their negative checks wait 6. - Sentry is global, so a second live Spring context in an OpenTelemetry suite takes it over. Keep one live context per JVM (`@DirtiesContext` when a test needs its own). - The async profiler rejects a traces path with a `.` anywhere in its absolute form. Put the path diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 5f29f0289a2..e11ae4c72b3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -5,11 +5,13 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder; import io.sentry.InitPriority; import io.sentry.Sentry; import io.sentry.SentryOptions; import io.sentry.internal.ManifestVersionReader; import io.sentry.protocol.SdkVersion; +import java.time.Duration; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -106,7 +108,16 @@ private SdkTracerProviderBuilder configureSdkTracerProvider( return tracerProvider .setSampler(new SentrySampler()) .addSpanProcessor(new OtelSentrySpanProcessor()) - .addSpanProcessor(BatchSpanProcessor.builder(new SentrySpanExporter()).build()); + .addSpanProcessor(createBatchSpanProcessor(config)); + } + + private BatchSpanProcessor createBatchSpanProcessor(ConfigProperties config) { + final BatchSpanProcessorBuilder builder = BatchSpanProcessor.builder(new SentrySpanExporter()); + final @Nullable Duration scheduleDelay = config.getDuration("otel.bsp.schedule.delay"); + if (scheduleDelay != null) { + builder.setScheduleDelay(scheduleDelay); + } + return builder.build(); } private Map getDefaultProperties() { diff --git a/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/AgentApp.kt b/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/AgentApp.kt index 82961625b3d..d7a7084a74c 100644 --- a/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/AgentApp.kt +++ b/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/AgentApp.kt @@ -40,6 +40,8 @@ object AgentApp { "SENTRY_PROFILE_LIFECYCLE" to "TRACE", "SENTRY_PROFILING_TRACES_DIR_PATH" to "${System.getProperty("java.io.tmpdir")}/sentry-integration-test/profiling-traces", + // Tests wait for Sentry's span batches, which are 5 seconds apart by default. + "OTEL_BSP_SCHEDULE_DELAY" to "100ms", "OTEL_TRACES_EXPORTER" to "none", "OTEL_METRICS_EXPORTER" to "none", "OTEL_LOGS_EXPORTER" to "none", diff --git a/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/DistributedTracingIntegrationTest.kt b/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/DistributedTracingIntegrationTest.kt index 353e6d62fd8..ad7025146b6 100644 --- a/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/DistributedTracingIntegrationTest.kt +++ b/sentry-spring-boot-4/src/integrationTestOtelAgent/kotlin/io/sentry/spring/boot4/otelagentintegrationtest/DistributedTracingIntegrationTest.kt @@ -47,8 +47,8 @@ class DistributedTracingIntegrationTest : IntegrationTest() { ) assertThat(status).isEqualTo(500) - // Longer than the 5 seconds the app's spans can wait for their batch to be exported. - server.assertNoEnvelope(millis = 6_000) { isForRequest(it, id) } + // Ten times the app's batch delay, so the request's spans would have been exported. + server.assertNoEnvelope(millis = 1_000) { isForRequest(it, id) } } @Test From cc1295a2b56525f76dc80ce31569cf6d1a3cd06c Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Tue, 27 Oct 2026 11:42:53 +0100 Subject: [PATCH 2/2] changelog Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 771b47adf25..9a612c24019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Support Navigation 3 integration for multi-Activity and multi-Fragment applications, where each Activity or Fragment has its own `SentryNavEffect` ([#6305](https://github.com/getsentry/sentry-java/pull/6305)) - See the [Navigation for Android docs](https://docs.sentry.io/platforms/android/integrations/navigation/#supported-setups) for a detailed list of supported vs unsupported app setups. +- The OpenTelemetry agent now honors `OTEL_BSP_SCHEDULE_DELAY` (`otel.bsp.schedule.delay`) for how often it sends spans to Sentry, instead of always waiting 5 seconds ([#6388](https://github.com/getsentry/sentry-java/pull/6388)) ### Fixes