Skip to content

Commit

Permalink
Add sample rate to baggage as well as trace in envelope header and fl…
Browse files Browse the repository at this point in the history
…atten user (#2135)

* Add sample rate to baggage and trace in envelope header; flatten user

* Add changelog

* Use _ for baggage keys

* Commit tests

* Feat/traces sampler into sample rate (#2141)

* Commit tests

* Add sample rate from traces sampler to DSC

* Do not replace null with true/false

* Restore sample rate in OutboxSender

* Remove fallback for sampling decision from TraceContext

* Remove sample rate fallback from TracesSamplingDecision

* Test more envelope header trace fields for OutboxSender

* CR changes

* Fix changelog

* Only send userid in Dynamic Sampling Context if sendDefaultPii is true (#2147)

* Skip sending userId in DSC if send default pii is off

* Add changelog

* Add test case
  • Loading branch information
adinauer committed Jul 1, 2022
1 parent 413862e commit fa3886f
Show file tree
Hide file tree
Showing 39 changed files with 819 additions and 275 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,10 +2,15 @@

## Unreleased

### Fixes

- Only send userid in Dynamic Sampling Context if sendDefaultPii is true ([#2147](https://github.com/getsentry/sentry-java/pull/2147))

### Features

- New package `sentry-android-navigation` for AndroidX Navigation support ([#2136](https://github.com/getsentry/sentry-java/pull/2136))
- New package `sentry-compose` for Jetpack Compose support (Navigation) ([#2136](https://github.com/getsentry/sentry-java/pull/2136))
- Add sample rate to baggage as well as trace in envelope header and flatten user ([#2135](https://github.com/getsentry/sentry-java/pull/2135))

## 6.1.4

Expand Down
Expand Up @@ -6,6 +6,7 @@ import com.nhaarman.mockitokotlin2.whenever
import io.sentry.Hint
import io.sentry.IHub
import io.sentry.SentryTracer
import io.sentry.TracesSamplingDecision
import io.sentry.TransactionContext
import io.sentry.android.core.ActivityLifecycleIntegration.UI_LOAD_OP
import io.sentry.protocol.MeasurementValue
Expand All @@ -21,7 +22,7 @@ class PerformanceAndroidEventProcessorTest {
val options = SentryAndroidOptions()

val hub = mock<IHub>()
val context = TransactionContext("name", "op", true)
val context = TransactionContext("name", "op", TracesSamplingDecision(true))
val tracer = SentryTracer(context, hub)
val activityFramesTracker = mock<ActivityFramesTracker>()

Expand Down
Expand Up @@ -16,6 +16,7 @@ import io.sentry.SentryTraceHeader
import io.sentry.SentryTracer
import io.sentry.SpanStatus
import io.sentry.TraceContext
import io.sentry.TracesSamplingDecision
import io.sentry.TransactionContext
import io.sentry.protocol.SentryTransaction
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -200,7 +201,7 @@ class SentryApolloInterceptorTest {
private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true) = runBlocking {
var tx: ITransaction? = null
if (isSpanActive) {
tx = SentryTracer(TransactionContext("op", "desc", true), fixture.hub)
tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub)
whenever(fixture.hub.span).thenReturn(tx)
}

Expand Down
Expand Up @@ -11,6 +11,7 @@ import io.sentry.SentryOptions
import io.sentry.SentryTraceHeader
import io.sentry.SentryTracer
import io.sentry.SpanStatus
import io.sentry.TracesSamplingDecision
import io.sentry.TransactionContext
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
Expand All @@ -28,7 +29,7 @@ class SentrySpanRestTemplateCustomizerTest {
val hub = mock<IHub>()
val restTemplate = RestTemplateBuilder().build()
var mockServer = MockWebServer()
val transaction = SentryTracer(TransactionContext("aTransaction", "op", true), hub)
val transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub)
internal val customizer = SentrySpanRestTemplateCustomizer(hub)
val url = mockServer.url("/test/123").toString()

Expand Down
Expand Up @@ -12,6 +12,7 @@ import io.sentry.SentryOptions
import io.sentry.SentryTraceHeader
import io.sentry.SentryTracer
import io.sentry.SpanStatus
import io.sentry.TracesSamplingDecision
import io.sentry.TransactionContext
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
Expand All @@ -34,7 +35,7 @@ class SentrySpanWebClientCustomizerTest {
lateinit var sentryOptions: SentryOptions
val hub = mock<IHub>()
var mockServer = MockWebServer()
val transaction = SentryTracer(TransactionContext("aTransaction", "op", true), hub)
val transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub)
private val customizer = SentrySpanWebClientCustomizer(hub)

fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient {
Expand Down
56 changes: 32 additions & 24 deletions sentry/api/sentry.api
Expand Up @@ -30,6 +30,7 @@ public final class io/sentry/Baggage {
public fun setEnvironment (Ljava/lang/String;)V
public fun setPublicKey (Ljava/lang/String;)V
public fun setRelease (Ljava/lang/String;)V
public fun setSampleRate (Ljava/lang/String;)V
public fun setTraceId (Ljava/lang/String;)V
public fun setTransaction (Ljava/lang/String;)V
public fun setUserId (Ljava/lang/String;)V
Expand Down Expand Up @@ -473,6 +474,7 @@ public abstract interface class io/sentry/ITransaction : io/sentry/ISpan {
public abstract fun getEventId ()Lio/sentry/protocol/SentryId;
public abstract fun getLatestActiveSpan ()Lio/sentry/Span;
public abstract fun getName ()Ljava/lang/String;
public abstract fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public abstract fun getSpans ()Ljava/util/List;
public abstract fun isSampled ()Ljava/lang/Boolean;
public abstract fun scheduleFinish ()V
Expand Down Expand Up @@ -660,6 +662,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction {
public fun getLatestActiveSpan ()Lio/sentry/Span;
public fun getName ()Ljava/lang/String;
public fun getOperation ()Ljava/lang/String;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getSpanContext ()Lio/sentry/SpanContext;
public fun getSpans ()Ljava/util/List;
public fun getStatus ()Lio/sentry/SpanStatus;
Expand Down Expand Up @@ -1386,6 +1389,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction {
public fun getLatestActiveSpan ()Lio/sentry/Span;
public fun getName ()Ljava/lang/String;
public fun getOperation ()Ljava/lang/String;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getSpanContext ()Lio/sentry/SpanContext;
public fun getSpans ()Ljava/util/List;
public fun getStartTimestamp ()Ljava/util/Date;
Expand Down Expand Up @@ -1488,6 +1492,7 @@ public final class io/sentry/Span : io/sentry/ISpan {
public fun getHighPrecisionTimestamp ()Ljava/lang/Double;
public fun getOperation ()Ljava/lang/String;
public fun getParentSpanId ()Lio/sentry/SpanId;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getSpanContext ()Lio/sentry/SpanContext;
public fun getSpanId ()Lio/sentry/SpanId;
public fun getStartTimestamp ()Ljava/util/Date;
Expand Down Expand Up @@ -1520,14 +1525,15 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU
protected field status Lio/sentry/SpanStatus;
protected field tags Ljava/util/Map;
public fun <init> (Lio/sentry/SpanContext;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;Lio/sentry/SpanStatus;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/String;Lio/sentry/SpanId;Ljava/lang/Boolean;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Ljava/lang/String;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;Lio/sentry/SpanStatus;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;)V
public fun <init> (Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Ljava/lang/Boolean;)V
public fun <init> (Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V
public fun getDescription ()Ljava/lang/String;
public fun getOperation ()Ljava/lang/String;
public fun getParentSpanId ()Lio/sentry/SpanId;
public fun getSampled ()Ljava/lang/Boolean;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getSpanId ()Lio/sentry/SpanId;
public fun getStatus ()Lio/sentry/SpanStatus;
public fun getTags ()Ljava/util/Map;
Expand All @@ -1537,6 +1543,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU
public fun setDescription (Ljava/lang/String;)V
public fun setOperation (Ljava/lang/String;)V
public fun setSampled (Ljava/lang/Boolean;)V
public fun setSamplingDecision (Lio/sentry/TracesSamplingDecision;)V
public fun setStatus (Lio/sentry/SpanStatus;)V
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
public fun setUnknown (Ljava/util/Map;)V
Expand Down Expand Up @@ -1619,10 +1626,12 @@ public final class io/sentry/TraceContext : io/sentry/JsonSerializable, io/sentr
public fun getEnvironment ()Ljava/lang/String;
public fun getPublicKey ()Ljava/lang/String;
public fun getRelease ()Ljava/lang/String;
public fun getSampleRate ()Ljava/lang/String;
public fun getTraceId ()Lio/sentry/protocol/SentryId;
public fun getTransaction ()Ljava/lang/String;
public fun getUnknown ()Ljava/util/Map;
public fun getUser ()Lio/sentry/TraceContext$TraceContextUser;
public fun getUserId ()Ljava/lang/String;
public fun getUserSegment ()Ljava/lang/String;
public fun serialize (Lio/sentry/JsonObjectWriter;Lio/sentry/ILogger;)V
public fun setUnknown (Ljava/util/Map;)V
public fun toBaggage (Lio/sentry/ILogger;)Lio/sentry/Baggage;
Expand All @@ -1638,31 +1647,20 @@ public final class io/sentry/TraceContext$JsonKeys {
public static final field ENVIRONMENT Ljava/lang/String;
public static final field PUBLIC_KEY Ljava/lang/String;
public static final field RELEASE 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;
public static final field USER Ljava/lang/String;
public static final field USER_ID Ljava/lang/String;
public static final field USER_SEGMENT Ljava/lang/String;
public fun <init> ()V
}

public final class io/sentry/TraceContext$TraceContextUser : io/sentry/JsonSerializable, io/sentry/JsonUnknown {
public fun <init> (Lio/sentry/protocol/User;)V
public fun getId ()Ljava/lang/String;
public fun getSegment ()Ljava/lang/String;
public fun getUnknown ()Ljava/util/Map;
public fun serialize (Lio/sentry/JsonObjectWriter;Lio/sentry/ILogger;)V
public fun setUnknown (Ljava/util/Map;)V
}

public final class io/sentry/TraceContext$TraceContextUser$Deserializer : io/sentry/JsonDeserializer {
public fun <init> ()V
public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/TraceContext$TraceContextUser;
public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object;
}

public final class io/sentry/TraceContext$TraceContextUser$JsonKeys {
public static final field ID Ljava/lang/String;
public static final field SEGMENT Ljava/lang/String;
public fun <init> ()V
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 getSampleRate ()Ljava/lang/Double;
public fun getSampled ()Ljava/lang/Boolean;
}

public final class io/sentry/TracingOrigins {
Expand All @@ -1673,10 +1671,11 @@ public final class io/sentry/TracingOrigins {

public final class io/sentry/TransactionContext : io/sentry/SpanContext {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Boolean;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V
public static fun fromSentryTrace (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext;
public fun getName ()Ljava/lang/String;
public fun getParentSampled ()Ljava/lang/Boolean;
public fun getParentSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun setParentSampled (Ljava/lang/Boolean;)V
}

Expand Down Expand Up @@ -2826,6 +2825,7 @@ public final class io/sentry/protocol/SentryTransaction : io/sentry/SentryBaseEv
public fun <init> (Lio/sentry/SentryTracer;)V
public fun <init> (Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Double;Ljava/util/List;Ljava/util/Map;)V
public fun getMeasurements ()Ljava/util/Map;
public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision;
public fun getSpans ()Ljava/util/List;
public fun getStartTimestamp ()Ljava/lang/Double;
public fun getStatus ()Lio/sentry/SpanStatus;
Expand Down Expand Up @@ -3035,6 +3035,14 @@ public final class io/sentry/util/Platform {
public static fun isJvm ()Z
}

public final class io/sentry/util/SampleRateUtil {
public fun <init> ()V
public static fun isValidSampleRate (Ljava/lang/Double;)Z
public static fun isValidSampleRate (Ljava/lang/Double;Z)Z
public static fun isValidTracesSampleRate (Ljava/lang/Double;)Z
public static fun isValidTracesSampleRate (Ljava/lang/Double;Z)Z
}

public final class io/sentry/util/StringUtils {
public static fun byteCountToString (J)Ljava/lang/String;
public static fun calculateStringHash (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/String;
Expand Down
12 changes: 8 additions & 4 deletions sentry/src/main/java/io/sentry/Baggage.java
Expand Up @@ -153,11 +153,11 @@ private static String decode(final @NotNull String value) throws UnsupportedEnco
}

public void setTraceId(final @Nullable String traceId) {
set("sentry-traceid", traceId);
set("sentry-trace_id", traceId);
}

public void setPublicKey(final @Nullable String publicKey) {
set("sentry-publickey", publicKey);
set("sentry-public_key", publicKey);
}

public void setEnvironment(final @Nullable String environment) {
Expand All @@ -169,17 +169,21 @@ public void setRelease(final @Nullable String release) {
}

public void setUserId(final @Nullable String userId) {
set("sentry-userid", userId);
set("sentry-user_id", userId);
}

public void setUserSegment(final @Nullable String userSegment) {
set("sentry-usersegment", userSegment);
set("sentry-user_segment", userSegment);
}

public void setTransaction(final @Nullable String transaction) {
set("sentry-transaction", transaction);
}

public void setSampleRate(final @Nullable String sampleRate) {
set("sentry-sample_rate", sampleRate);
}

public void set(final @NotNull String key, final @Nullable String value) {
this.keyValues.put(key, value);
}
Expand Down
6 changes: 3 additions & 3 deletions sentry/src/main/java/io/sentry/Hub.java
Expand Up @@ -730,8 +730,8 @@ public void flush(long timeoutMillis) {
} else {
final SamplingContext samplingContext =
new SamplingContext(transactionContext, customSamplingContext);
boolean samplingDecision = tracesSampler.sample(samplingContext);
transactionContext.setSampled(samplingDecision);
@NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext);
transactionContext.setSamplingDecision(samplingDecision);

transaction =
new SentryTracer(
Expand All @@ -745,7 +745,7 @@ public void flush(long timeoutMillis) {

// The listener is called only if the transaction exists, as the transaction is needed to
// stop it
if (samplingDecision && options.isProfilingEnabled()) {
if (samplingDecision.getSampled() && options.isProfilingEnabled()) {
final ITransactionProfiler transactionProfiler = options.getTransactionProfiler();
transactionProfiler.onTransactionStart(transaction);
}
Expand Down
3 changes: 3 additions & 0 deletions sentry/src/main/java/io/sentry/ITransaction.java
Expand Up @@ -35,6 +35,9 @@ public interface ITransaction extends ISpan {
@Nullable
Boolean isSampled();

@Nullable
TracesSamplingDecision getSamplingDecision();

/**
* Returns the latest span that is not finished.
*
Expand Down
5 changes: 5 additions & 0 deletions sentry/src/main/java/io/sentry/NoOpTransaction.java
Expand Up @@ -136,6 +136,11 @@ public void setTag(@NotNull String key, @NotNull String value) {}
return null;
}

@Override
public @Nullable TracesSamplingDecision getSamplingDecision() {
return null;
}

@Override
public void setData(@NotNull String key, @NotNull Object value) {}

Expand Down
41 changes: 37 additions & 4 deletions sentry/src/main/java/io/sentry/OutboxSender.java
Expand Up @@ -13,6 +13,7 @@
import io.sentry.util.HintUtils;
import io.sentry.util.LogUtils;
import io.sentry.util.Objects;
import io.sentry.util.SampleRateUtil;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -161,12 +162,17 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN
continue;
}

// if there is no trace context header we also won't send it to Sentry
final @Nullable TraceContext traceContext = envelope.getHeader().getTraceContext();
if (transaction.getContexts().getTrace() != null) {
// Hint: Set sampled in order for the transaction not to be dropped, as this is a
// transient property.
transaction.getContexts().getTrace().setSampled(true);
// Hint: Set sampling decision in order for the transaction not to be dropped, as this
// is a transient property.
transaction
.getContexts()
.getTrace()
.setSamplingDecision(extractSamplingDecision(traceContext));
}
hub.captureTransaction(transaction, envelope.getHeader().getTraceContext(), hint);
hub.captureTransaction(transaction, traceContext, hint);
logItemCaptured(currentItem);

if (!waitFlush(hint)) {
Expand Down Expand Up @@ -216,6 +222,33 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN
}
}

private @NotNull TracesSamplingDecision extractSamplingDecision(
final @Nullable TraceContext traceContext) {
if (traceContext != null) {
final @Nullable String sampleRateString = traceContext.getSampleRate();
if (sampleRateString != null) {
try {
final Double sampleRate = Double.parseDouble(sampleRateString);
if (!SampleRateUtil.isValidTracesSampleRate(sampleRate, false)) {
logger.log(
SentryLevel.ERROR,
"Invalid sample rate parsed from TraceContext: %s",
sampleRateString);
} else {
return new TracesSamplingDecision(true, sampleRate);
}
} catch (Exception e) {
logger.log(
SentryLevel.ERROR,
"Unable to parse sample rate from TraceContext: %s",
sampleRateString);
}
}
}

return new TracesSamplingDecision(true);
}

private void logEnvelopeItemNull(final @NotNull SentryEnvelopeItem item, int itemIndex) {
logger.log(
SentryLevel.ERROR,
Expand Down

0 comments on commit fa3886f

Please sign in to comment.