Skip to content

Commit

Permalink
Propagate sampling random value (#4153)
Browse files Browse the repository at this point in the history
* wip

* wip2

* wip3

* format + api

* cleanup

* revert change to demo

* make baggage final again

* remove ObjectToString suppressing

* remove outdated comment

* remove getPropagationContext from Scopes again

* remove noop baggage and propagation context again

* fix outbox sender; test; cleanup api file

* review changes

* fix test for old span processor

* review feedback

* Format code

* changelog

---------

Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
  • Loading branch information
adinauer and getsentry-bot authored Feb 12, 2025
1 parent 367d8b9 commit dc85168
Show file tree
Hide file tree
Showing 39 changed files with 881 additions and 213 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Expose new `withSentryObservableEffect` method overload that accepts `SentryNavigationListener` as a parameter ([#4143](https://github.com/getsentry/sentry-java/pull/4143))
- This allows sharing the same `SentryNavigationListener` instance across fragments and composables to preserve the trace
- (Internal) Add API to filter native debug images based on stacktrace addresses ([#4089](https://github.com/getsentry/sentry-java/pull/4089))
- Propagate sampling random value ([#4153](https://github.com/getsentry/sentry-java/pull/4153))
- The random value used for sampling traces is now sent to Sentry and attached to the `baggage` header on outgoing requests

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes {
public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey;
public static final field PROFILE_SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLED Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLE_RAND Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey;
public fun <init> ()V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public final class InternalSemanticAttributes {
public static final AttributeKey<Boolean> SAMPLED = AttributeKey.booleanKey("sentry.sampled");
public static final AttributeKey<Double> SAMPLE_RATE =
AttributeKey.doubleKey("sentry.sample_rate");
public static final AttributeKey<Double> SAMPLE_RAND =
AttributeKey.doubleKey("sentry.sample_rand");
public static final AttributeKey<Boolean> PARENT_SAMPLED =
AttributeKey.booleanKey("sentry.parent_sampled");
public static final AttributeKey<Boolean> PROFILE_SAMPLED =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public OtelSpanFactory() {
spanBuilder.setAttribute(InternalSemanticAttributes.SAMPLED, samplingDecision.getSampled());
spanBuilder.setAttribute(
InternalSemanticAttributes.SAMPLE_RATE, samplingDecision.getSampleRate());
spanBuilder.setAttribute(
InternalSemanticAttributes.SAMPLE_RAND, samplingDecision.getSampleRand());
spanBuilder.setAttribute(
InternalSemanticAttributes.PROFILE_SAMPLED, samplingDecision.getProfileSampled());
spanBuilder.setAttribute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ public final class OtelSamplingUtil {
final @Nullable Boolean sampled = attributes.get(InternalSemanticAttributes.SAMPLED);
if (sampled != null) {
final @Nullable Double sampleRate = attributes.get(InternalSemanticAttributes.SAMPLE_RATE);
final @Nullable Double sampleRand = attributes.get(InternalSemanticAttributes.SAMPLE_RAND);
final @Nullable Boolean profileSampled =
attributes.get(InternalSemanticAttributes.PROFILE_SAMPLED);
final @Nullable Double profileSampleRate =
attributes.get(InternalSemanticAttributes.PROFILE_SAMPLE_RATE);

return new TracesSamplingDecision(
sampled, sampleRate, profileSampled == null ? false : profileSampled, profileSampleRate);
sampled,
sampleRate,
sampleRand,
profileSampled == null ? false : profileSampled,
profileSampleRate);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
import io.sentry.Baggage;
import io.sentry.BaggageHeader;
import io.sentry.IScopes;
import io.sentry.PropagationContext;
import io.sentry.ScopesAdapter;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
import io.sentry.SentryTraceHeader;
import io.sentry.exception.InvalidSentryTraceHeaderException;
import io.sentry.util.TracingUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -73,12 +73,17 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
return;
}

final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace();
setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue());
final @Nullable BaggageHeader baggageHeader =
sentrySpan.toBaggageHeader(Collections.emptyList());
if (baggageHeader != null) {
setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue());
// TODO can we use traceIfAllowed? do we have the URL here? need to access span attrs
final @Nullable TracingUtils.TracingHeaders tracingHeaders =
TracingUtils.trace(scopes, Collections.emptyList(), sentrySpan);

if (tracingHeaders != null) {
final @NotNull SentryTraceHeader sentryTraceHeader = tracingHeaders.getSentryTraceHeader();
setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue());
final @Nullable BaggageHeader baggageHeader = tracingHeaders.getBaggageHeader();
if (baggageHeader != null) {
setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue());
}
}
}

Expand Down Expand Up @@ -125,11 +130,6 @@ public <C> Context extract(
.getLogger()
.log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId());

final @NotNull PropagationContext propagationContext =
PropagationContext.fromHeaders(
scopes.getOptions().getLogger(), sentryTraceString, baggageString);
scopesToUse.getIsolationScope().setPropagationContext(propagationContext);

return modifiedContext;
} catch (InvalidSentryTraceHeaderException e) {
scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,10 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
baggage = baggageFromContext;
}

final @Nullable Boolean baggageMutable =
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE);
final @Nullable String baggageString =
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE);
if (baggageString != null) {
baggage = Baggage.fromHeader(baggageString);
if (baggageMutable == true) {
baggage.freeze();
}
}

final @Nullable Boolean sampled = isSampled(otelSpan, samplingDecision);
Expand All @@ -87,6 +82,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
new PropagationContext(
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);

baggage = propagationContext.getBaggage();

updatePropagationContext(scopes, propagationContext);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public final class OtelSpanWrapper implements IOtelSpanWrapper {
private final @NotNull Contexts contexts = new Contexts();
private @Nullable String transactionName;
private @Nullable TransactionNameSource transactionNameSource;
private final @Nullable Baggage baggage;
private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock();

private final @NotNull Map<String, Object> data = new ConcurrentHashMap<>();
Expand All @@ -86,17 +85,12 @@ public OtelSpanWrapper(
this.scopes = Objects.requireNonNull(scopes, "scopes are required");
this.span = new WeakReference<>(span);
this.startTimestamp = startTimestamp;

if (parentSpan != null) {
this.baggage = parentSpan.getSpanContext().getBaggage();
} else if (baggage != null) {
this.baggage = baggage;
} else {
this.baggage = null;
}

final @Nullable Baggage baggageToUse =
baggage != null
? baggage
: (parentSpan != null ? parentSpan.getSpanContext().getBaggage() : null);
this.context =
new OtelSpanContext(span, samplingDecision, parentSpan, parentSpanId, this.baggage);
new OtelSpanContext(span, samplingDecision, parentSpan, parentSpanId, baggageToUse);
}

@Override
Expand Down Expand Up @@ -207,15 +201,16 @@ public OtelSpanWrapper(
@Override
public @Nullable TraceContext traceContext() {
if (scopes.getOptions().isTraceSampling()) {
final @Nullable Baggage baggage = context.getBaggage();
if (baggage != null) {
updateBaggageValues();
updateBaggageValues(baggage);
return baggage.toTraceContext();
}
}
return null;
}

private void updateBaggageValues() {
private void updateBaggageValues(final @NotNull Baggage baggage) {
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
if (baggage != null && baggage.isMutable()) {
final AtomicReference<SentryId> replayIdAtomicReference = new AtomicReference<>();
Expand All @@ -238,8 +233,9 @@ private void updateBaggageValues() {
@Override
public @Nullable BaggageHeader toBaggageHeader(@Nullable List<String> thirdPartyBaggageHeaders) {
if (scopes.getOptions().isTraceSampling()) {
final @Nullable Baggage baggage = context.getBaggage();
if (baggage != null) {
updateBaggageValues();
updateBaggageValues(baggage);
return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public SamplingResult shouldSample(
scopes
.getOptions()
.getInternalTracesSampler()
.sample(new SamplingContext(transactionContext, null));
.sample(
new SamplingContext(transactionContext, null, propagationContext.getSampleRand()));

if (!sentryDecision.getSampled()) {
scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public Attributes getAttributes() {
return Attributes.builder()
.put(InternalSemanticAttributes.SAMPLED, sentryDecision.getSampled())
.put(InternalSemanticAttributes.SAMPLE_RATE, sentryDecision.getSampleRate())
.put(InternalSemanticAttributes.SAMPLE_RAND, sentryDecision.getSampleRand())
.put(InternalSemanticAttributes.PROFILE_SAMPLED, sentryDecision.getProfileSampled())
.put(InternalSemanticAttributes.PROFILE_SAMPLE_RATE, sentryDecision.getProfileSampleRate())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public final class SentrySpanExporter implements SpanExporter {
InternalSemanticAttributes.BAGGAGE_MUTABLE.getKey(),
InternalSemanticAttributes.SAMPLED.getKey(),
InternalSemanticAttributes.SAMPLE_RATE.getKey(),
InternalSemanticAttributes.SAMPLE_RAND.getKey(),
InternalSemanticAttributes.PROFILE_SAMPLED.getKey(),
InternalSemanticAttributes.PROFILE_SAMPLE_RATE.getKey(),
InternalSemanticAttributes.PARENT_SAMPLED.getKey(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,15 @@ class SentrySpanProcessorTest {
assertEquals("1", it.baggage?.sampleRate)
assertEquals("HTTP GET", it.baggage?.transaction)
assertEquals("502f25099c204a2fbf4cb16edc5975d1", it.baggage?.publicKey)
assertFalse(it.baggage!!.isMutable)
} else {
assertNotNull(it.baggage)
assertNull(it.baggage?.traceId)
assertNull(it.baggage?.sampleRate)
assertNull(it.baggage?.transaction)
assertNull(it.baggage?.publicKey)
assertFalse(it.baggage!!.isMutable)
assertTrue(it.baggage!!.isMutable)
}
assertFalse(it.baggage!!.isMutable)
},
check<TransactionOptions> {
assertNotNull(it.startTimestamp)
Expand All @@ -434,7 +434,7 @@ class SentrySpanProcessorTest {
assertEquals(otelSpan.spanContext.traceId, it.traceId.toString())
assertNull(it.parentSpanId)
assertNull(it.parentSamplingDecision)
assertNull(it.baggage)
assertNotNull(it.baggage)
},
check<TransactionOptions> {
assertNotNull(it.startTimestamp)
Expand Down
21 changes: 19 additions & 2 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public abstract interface class io/sentry/BackfillingEventProcessor : io/sentry/
public final class io/sentry/Baggage {
public fun <init> (Lio/sentry/Baggage;)V
public fun <init> (Lio/sentry/ILogger;)V
public fun <init> (Ljava/util/Map;Ljava/lang/String;ZLio/sentry/ILogger;)V
public fun <init> (Ljava/util/Map;Ljava/lang/String;ZZLio/sentry/ILogger;)V
public fun freeze ()V
public static fun fromEvent (Lio/sentry/SentryEvent;Lio/sentry/SentryOptions;)Lio/sentry/Baggage;
public static fun fromHeader (Ljava/lang/String;)Lio/sentry/Baggage;
Expand All @@ -46,6 +46,8 @@ public final class io/sentry/Baggage {
public fun getPublicKey ()Ljava/lang/String;
public fun getRelease ()Ljava/lang/String;
public fun getReplayId ()Ljava/lang/String;
public fun getSampleRand ()Ljava/lang/String;
public fun getSampleRandDouble ()Ljava/lang/Double;
public fun getSampleRate ()Ljava/lang/String;
public fun getSampleRateDouble ()Ljava/lang/Double;
public fun getSampled ()Ljava/lang/String;
Expand All @@ -55,11 +57,14 @@ public final class io/sentry/Baggage {
public fun getUnknown ()Ljava/util/Map;
public fun getUserId ()Ljava/lang/String;
public fun isMutable ()Z
public fun isShouldFreeze ()Z
public fun set (Ljava/lang/String;Ljava/lang/String;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setPublicKey (Ljava/lang/String;)V
public fun setRelease (Ljava/lang/String;)V
public fun setReplayId (Ljava/lang/String;)V
public fun setSampleRand (Ljava/lang/String;)V
public fun setSampleRandDouble (Ljava/lang/Double;)V
public fun setSampleRate (Ljava/lang/String;)V
public fun setSampled (Ljava/lang/String;)V
public fun setTraceId (Ljava/lang/String;)V
Expand All @@ -78,6 +83,7 @@ public final class io/sentry/Baggage$DSCKeys {
public static final field RELEASE Ljava/lang/String;
public static final field REPLAY_ID Ljava/lang/String;
public static final field SAMPLED Ljava/lang/String;
public static final field SAMPLE_RAND Ljava/lang/String;
public static final field SAMPLE_RATE Ljava/lang/String;
public static final field TRACE_ID Ljava/lang/String;
public static final field TRANSACTION Ljava/lang/String;
Expand Down Expand Up @@ -1947,10 +1953,10 @@ public final class io/sentry/PropagationContext {
public static fun fromHeaders (Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;)Lio/sentry/PropagationContext;
public fun getBaggage ()Lio/sentry/Baggage;
public fun getParentSpanId ()Lio/sentry/SpanId;
public fun getSampleRand ()Ljava/lang/Double;
public fun getSpanId ()Lio/sentry/SpanId;
public fun getTraceId ()Lio/sentry/protocol/SentryId;
public fun isSampled ()Ljava/lang/Boolean;
public fun setBaggage (Lio/sentry/Baggage;)V
public fun setParentSpanId (Lio/sentry/SpanId;)V
public fun setSampled (Ljava/lang/Boolean;)V
public fun setSpanId (Lio/sentry/SpanId;)V
Expand Down Expand Up @@ -2007,7 +2013,9 @@ public final class io/sentry/RequestDetails {

public final class io/sentry/SamplingContext {
public fun <init> (Lio/sentry/TransactionContext;Lio/sentry/CustomSamplingContext;)V
public fun <init> (Lio/sentry/TransactionContext;Lio/sentry/CustomSamplingContext;Ljava/lang/Double;)V
public fun getCustomSamplingContext ()Lio/sentry/CustomSamplingContext;
public fun getSampleRand ()Ljava/lang/Double;
public fun getTransactionContext ()Lio/sentry/TransactionContext;
}

Expand Down Expand Up @@ -3634,6 +3642,7 @@ public final class io/sentry/TraceContext : io/sentry/JsonSerializable, io/sentr
public fun getPublicKey ()Ljava/lang/String;
public fun getRelease ()Ljava/lang/String;
public fun getReplayId ()Lio/sentry/protocol/SentryId;
public fun getSampleRand ()Ljava/lang/String;
public fun getSampleRate ()Ljava/lang/String;
public fun getSampled ()Ljava/lang/String;
public fun getTraceId ()Lio/sentry/protocol/SentryId;
Expand All @@ -3656,6 +3665,7 @@ public final class io/sentry/TraceContext$JsonKeys {
public static final field RELEASE Ljava/lang/String;
public static final field REPLAY_ID Ljava/lang/String;
public static final field SAMPLED Ljava/lang/String;
public static final field SAMPLE_RAND Ljava/lang/String;
public static final field SAMPLE_RATE Ljava/lang/String;
public static final field TRACE_ID Ljava/lang/String;
public static final field TRANSACTION Ljava/lang/String;
Expand All @@ -3672,8 +3682,11 @@ public final class io/sentry/TracesSamplingDecision {
public fun <init> (Ljava/lang/Boolean;)V
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Double;)V
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Double;Ljava/lang/Boolean;Ljava/lang/Double;)V
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Double;Ljava/lang/Double;)V
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Boolean;Ljava/lang/Double;)V
public fun getProfileSampleRate ()Ljava/lang/Double;
public fun getProfileSampled ()Ljava/lang/Boolean;
public fun getSampleRand ()Ljava/lang/Double;
public fun getSampleRate ()Ljava/lang/Double;
public fun getSampled ()Ljava/lang/Boolean;
}
Expand Down Expand Up @@ -6275,6 +6288,8 @@ public final class io/sentry/util/Random : java/io/Serializable {

public final class io/sentry/util/SampleRateUtils {
public fun <init> ()V
public static fun backfilledSampleRand (Lio/sentry/TracesSamplingDecision;)Lio/sentry/TracesSamplingDecision;
public static fun backfilledSampleRand (Ljava/lang/Double;Ljava/lang/Double;Ljava/lang/Boolean;)Ljava/lang/Double;
public static fun isValidProfilesSampleRate (Ljava/lang/Double;)Z
public static fun isValidSampleRate (Ljava/lang/Double;)Z
public static fun isValidTracesSampleRate (Ljava/lang/Double;)Z
Expand Down Expand Up @@ -6310,6 +6325,8 @@ public final class io/sentry/util/StringUtils {

public final class io/sentry/util/TracingUtils {
public fun <init> ()V
public static fun ensureBaggage (Lio/sentry/Baggage;Lio/sentry/TracesSamplingDecision;)Lio/sentry/Baggage;
public static fun ensureBaggage (Lio/sentry/Baggage;Ljava/lang/Boolean;Ljava/lang/Double;Ljava/lang/Double;)Lio/sentry/Baggage;
public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z
public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public static fun startNewTrace (Lio/sentry/IScopes;)V
Expand Down
Loading

0 comments on commit dc85168

Please sign in to comment.