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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
- Drop telemetry and record `callback_error` when a customer event processor throws instead of continuing with a potentially partially processed item. SDK-owned processor failures are logged and processing continues without a `callback_error` client report.
- Drop breadcrumbs when `beforeBreadcrumb` throws instead of storing exception details on the breadcrumb.
- When `tracesSampler` throws, drop the transaction and record `callback_error` instead of inheriting the parent sampling decision or falling back to `tracesSampleRate` ([#6163](https://github.com/getsentry/sentry-java/pull/6163))
- When `profilesSampler` throws, disable profiling instead of falling back to `profilesSampleRate` or inheriting the parent's profiling decision. Trace sampling is unchanged ([#6164](https://github.com/getsentry/sentry-java/pull/6164))
- Disable URL caching when reading `META-INF/MANIFEST.MF` files during version detection so that the SDK no longer keeps jar file handles open for the life of the process ([#6124](https://github.com/getsentry/sentry-java/pull/6124)
- Keep the `EventListener` wrapped by `SentryOkHttpEventListener` per `Call` ([#6003](https://github.com/getsentry/sentry-java/pull/6003))

Expand Down
11 changes: 10 additions & 1 deletion sentry/src/main/java/io/sentry/TracesSampler.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ public TracesSamplingDecision sample(final @NotNull SamplingContext samplingCont
}

Double profilesSampleRate = null;
boolean profilesSamplerFailed = false;
if (options.getProfilesSampler() != null) {
try {
profilesSampleRate = options.getProfilesSampler().sample(samplingContext);
} catch (Throwable t) {
profilesSamplerFailed = true;
options
.getLogger()
.log(SentryLevel.ERROR, "Error in the 'ProfilesSamplerCallback' callback.", t);
}
}
if (profilesSampleRate == null) {
if (profilesSampleRate == null && !profilesSamplerFailed) {
profilesSampleRate = options.getProfilesSampleRate();
}
Boolean profilesSampled = profilesSampleRate != null && sample(profilesSampleRate, sampleRand);
Expand Down Expand Up @@ -69,6 +71,13 @@ public TracesSamplingDecision sample(final @NotNull SamplingContext samplingCont
final TracesSamplingDecision parentSamplingDecision =
samplingContext.getTransactionContext().getParentSamplingDecision();
if (parentSamplingDecision != null) {
if (profilesSamplerFailed) {
return SampleRateUtils.backfilledSampleRand(
new TracesSamplingDecision(
parentSamplingDecision.getSampled(),
parentSamplingDecision.getSampleRate(),
parentSamplingDecision.getSampleRand()));
}
return SampleRateUtils.backfilledSampleRand(parentSamplingDecision);
}

Expand Down
92 changes: 83 additions & 9 deletions sentry/src/test/java/io/sentry/TracesSamplerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class TracesSamplerTest {

@Test
fun `when profilesSampler returns null and parentSampled is set sampler uses it as a sampling decision`() {
val sampler = fixture.getSut(tracesSampleRate = 1.0, profilesSamplerCallback = null)
val sampler = fixture.getSut(tracesSampleRate = 1.0, profilesSamplerCallback = { null })
val transactionContextParentSampled = TransactionContext("name", "op")
transactionContextParentSampled.setParentSampled(true, true)
val samplingDecision =
Expand Down Expand Up @@ -225,7 +225,7 @@ class TracesSamplerTest {
fixture.getSut(
tracesSampleRate = 1.0,
profilesSampleRate = 0.2,
profilesSamplerCallback = null,
profilesSamplerCallback = { null },
)
val samplingDecision =
sampler.sample(
Expand Down Expand Up @@ -358,18 +358,92 @@ class TracesSamplerTest {
}

@Test
fun `when a profilingRate and a ProfilesSamplerCallback is set but the callback throws an exception then profiling should still be enabled`() {
val exception = Exception("faulty ProfilesSamplerCallback")
fun `when profilesSampler throws then static profile rates are ignored`() {
for (profilesSampleRate in listOf(null, 0.0, 1.0)) {
val sampler =
fixture.getSut(
tracesSampleRate = 1.0,
profilesSampleRate = profilesSampleRate,
profilesSamplerCallback = {
throw IllegalStateException("faulty ProfilesSamplerCallback")
},
)
val decision =
sampler.sample(SamplingContext(TransactionContext("name", "op"), null, 0.0, null))

assertThat(decision.sampled).isTrue()
assertThat(decision.sampleRate).isEqualTo(1.0)
assertThat(decision.sampleRand).isEqualTo(0.0)
assertThat(decision.profileSampled).isFalse()
assertThat(decision.profileSampleRate).isNull()
}
}

@Test
fun `when profilesSampler throws then tracesSampler still determines trace sampling`() {
val sampler =
fixture.getSut(
tracesSampleRate = 1.0,
tracesSampleRate = 0.0,
profilesSampleRate = 1.0,
profilesSamplerCallback = { throw exception },
tracesSamplerCallback = { 0.5 },
profilesSamplerCallback = { throw IllegalStateException("faulty ProfilesSamplerCallback") },
)
val decision =
sampler.sample(SamplingContext(TransactionContext("name", "op"), null, 0.0, null))
assertTrue(decision.profileSampled)
assertEquals(0.0, decision.sampleRand)
sampler.sample(SamplingContext(TransactionContext("name", "op"), null, 0.1, null))

assertThat(decision.sampled).isTrue()
assertThat(decision.sampleRate).isEqualTo(0.5)
assertThat(decision.sampleRand).isEqualTo(0.1)
assertThat(decision.profileSampled).isFalse()
assertThat(decision.profileSampleRate).isNull()
}

@Test
fun `when profilesSampler throws then parent trace sampling is preserved without profiling`() {
val sampler =
fixture.getSut(
tracesSampleRate = 1.0,
profilesSampleRate = 1.0,
profilesSamplerCallback = { throw IllegalStateException("faulty ProfilesSamplerCallback") },
)
for (sampled in listOf(true, false)) {
val sampleRand = if (sampled) 0.1 else 0.9
val parentDecision = TracesSamplingDecision(sampled, 0.5, sampleRand, true, 1.0)
val transactionContext =
TransactionContext(SentryId(), SpanId(), SpanId(), parentDecision, null)

val decision = sampler.sample(SamplingContext(transactionContext, null, sampleRand, null))

assertThat(decision.sampled).isEqualTo(sampled)
assertThat(decision.sampleRate).isEqualTo(0.5)
assertThat(decision.sampleRand).isEqualTo(sampleRand)
assertThat(decision.profileSampled).isFalse()
assertThat(decision.profileSampleRate).isNull()
assertThat(parentDecision.profileSampled).isEqualTo(sampled)
assertThat(parentDecision.profileSampleRate).isEqualTo(1.0)
}
}

@Test
fun `when both samplers throw then tracing and profiling are disabled despite a sampled parent`() {
val sampler =
fixture.getSut(
tracesSampleRate = 0.0,
profilesSampleRate = 1.0,
tracesSamplerCallback = { throw IllegalStateException("faulty TracesSamplerCallback") },
profilesSamplerCallback = { throw IllegalStateException("faulty ProfilesSamplerCallback") },
)
val parentDecision = TracesSamplingDecision(true, 0.5, true, 1.0)
val transactionContext =
TransactionContext(SentryId(), SpanId(), SpanId(), parentDecision, null)

val decision = sampler.sample(SamplingContext(transactionContext, null, 0.9, null))

assertThat(decision.sampled).isFalse()
assertThat(decision.sampleRate).isNull()
assertThat(decision.sampleRand).isEqualTo(0.9)
assertThat(decision.profileSampled).isFalse()
assertThat(decision.profileSampleRate).isNull()
}

@Test
Expand Down
Loading