Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sample rate to baggage as well as trace in envelope header and flatten user #2135

Merged
merged 10 commits into from
Jul 1, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- 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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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