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

[SR] Allow overriding SdkVersion only for replay events #4014

Merged
merged 14 commits into from
Dec 30, 2024
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Fix warm start detection ([#3937](https://github.com/getsentry/sentry-java/pull/3937))

### Internal

- Session Replay: Allow overriding `SdkVersion` for replay events ([#4014](https://github.com/getsentry/sentry-java/pull/4014))

## 7.19.1

### Fixes
Expand Down
8 changes: 5 additions & 3 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public abstract interface class io/sentry/EventProcessor {
}

public final class io/sentry/ExperimentalOptions {
public fun <init> (Z)V
public fun <init> (ZLio/sentry/protocol/SdkVersion;)V
public fun getSessionReplay ()Lio/sentry/SentryReplayOptions;
public fun setSessionReplay (Lio/sentry/SentryReplayOptions;)V
}
Expand Down Expand Up @@ -2724,8 +2724,8 @@ public final class io/sentry/SentryReplayOptions {
public static final field TEXT_VIEW_CLASS_NAME Ljava/lang/String;
public static final field VIDEO_VIEW_CLASS_NAME Ljava/lang/String;
public static final field WEB_VIEW_CLASS_NAME Ljava/lang/String;
public fun <init> (Ljava/lang/Double;Ljava/lang/Double;)V
public fun <init> (Z)V
public fun <init> (Ljava/lang/Double;Ljava/lang/Double;Lio/sentry/protocol/SdkVersion;)V
public fun <init> (ZLio/sentry/protocol/SdkVersion;)V
public fun addMaskViewClass (Ljava/lang/String;)V
public fun addUnmaskViewClass (Ljava/lang/String;)V
public fun getErrorReplayDuration ()J
Expand All @@ -2734,6 +2734,7 @@ public final class io/sentry/SentryReplayOptions {
public fun getMaskViewContainerClass ()Ljava/lang/String;
public fun getOnErrorSampleRate ()Ljava/lang/Double;
public fun getQuality ()Lio/sentry/SentryReplayOptions$SentryReplayQuality;
public fun getSdkVersion ()Lio/sentry/protocol/SdkVersion;
public fun getSessionDuration ()J
public fun getSessionSampleRate ()Ljava/lang/Double;
public fun getSessionSegmentDuration ()J
Expand All @@ -2747,6 +2748,7 @@ public final class io/sentry/SentryReplayOptions {
public fun setMaskViewContainerClass (Ljava/lang/String;)V
public fun setOnErrorSampleRate (Ljava/lang/Double;)V
public fun setQuality (Lio/sentry/SentryReplayOptions$SentryReplayQuality;)V
public fun setSdkVersion (Lio/sentry/protocol/SdkVersion;)V
public fun setSessionSampleRate (Ljava/lang/Double;)V
public fun setTrackOrientationChange (Z)V
public fun setUnmaskViewContainerClass (Ljava/lang/String;)V
Expand Down
6 changes: 4 additions & 2 deletions sentry/src/main/java/io/sentry/ExperimentalOptions.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.sentry;

import io.sentry.protocol.SdkVersion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Experimental options for new features, these options are going to be promoted to SentryOptions
Expand All @@ -11,8 +13,8 @@
public final class ExperimentalOptions {
private @NotNull SentryReplayOptions sessionReplay;

public ExperimentalOptions(final boolean empty) {
this.sessionReplay = new SentryReplayOptions(empty);
public ExperimentalOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) {
this.sessionReplay = new SentryReplayOptions(empty, sdkVersion);
}

@NotNull
Expand Down
7 changes: 7 additions & 0 deletions sentry/src/main/java/io/sentry/MainEventProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.sentry.hints.Cached;
import io.sentry.protocol.DebugImage;
import io.sentry.protocol.DebugMeta;
import io.sentry.protocol.SdkVersion;
import io.sentry.protocol.SentryException;
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
Expand Down Expand Up @@ -159,6 +160,12 @@ private void processNonCachedEvent(final @NotNull SentryBaseEvent event) {

if (shouldApplyScopeData(event, hint)) {
processNonCachedEvent(event);
final @Nullable SdkVersion replaySdkVersion =
options.getExperimental().getSessionReplay().getSdkVersion();
if (replaySdkVersion != null) {
// we override the SdkVersion only for replay events as those may come from Hybrid SDKs
event.setSdk(replaySdkVersion);
}
}
return event;
}
Expand Down
5 changes: 4 additions & 1 deletion sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,11 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) {
envelopeItems.add(replayItem);
final SentryId sentryId = event.getEventId();

// SdkVersion from ReplayOptions defaults to SdkVersion from SentryOptions and can be
// overwritten by the hybrid SDKs
final SentryEnvelopeHeader envelopeHeader =
new SentryEnvelopeHeader(sentryId, options.getSdkVersion(), traceContext);
new SentryEnvelopeHeader(
sentryId, options.getExperimental().getSessionReplay().getSdkVersion(), traceContext);
romtsn marked this conversation as resolved.
Show resolved Hide resolved

return new SentryEnvelope(envelopeHeader, envelopeItems);
}
Expand Down
12 changes: 10 additions & 2 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,13 @@ public void setSslSocketFactory(final @Nullable SSLSocketFactory sslSocketFactor
*/
@ApiStatus.Internal
public void setSdkVersion(final @Nullable SdkVersion sdkVersion) {
final @Nullable SdkVersion replaySdkVersion = experimental.getSessionReplay().getSdkVersion();
if (this.sdkVersion != null
&& replaySdkVersion != null
&& this.sdkVersion.equals(replaySdkVersion)) {
// if sdkVersion = sessionReplay.sdkVersion we override it, as it means no one else set it
experimental.getSessionReplay().setSdkVersion(sdkVersion);
}
this.sdkVersion = sdkVersion;
}

Expand Down Expand Up @@ -2626,7 +2633,8 @@ public SentryOptions() {
* @param empty if options should be empty.
*/
private SentryOptions(final boolean empty) {
experimental = new ExperimentalOptions(empty);
final @NotNull SdkVersion sdkVersion = createSdkVersion();
experimental = new ExperimentalOptions(empty, sdkVersion);
if (!empty) {
// SentryExecutorService should be initialized before any
// SendCachedEventFireAndForgetIntegration
Expand All @@ -2647,7 +2655,7 @@ private SentryOptions(final boolean empty) {
}

setSentryClientName(BuildConfig.SENTRY_JAVA_SDK_NAME + "/" + BuildConfig.VERSION_NAME);
setSdkVersion(createSdkVersion());
setSdkVersion(sdkVersion);
addPackageInfo();
}
}
Expand Down
27 changes: 24 additions & 3 deletions sentry/src/main/java/io/sentry/SentryReplayOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry;

import io.sentry.protocol.SdkVersion;
import io.sentry.util.SampleRateUtils;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
Expand Down Expand Up @@ -114,7 +115,13 @@ public enum SentryReplayQuality {
*/
private boolean trackOrientationChange = true;

public SentryReplayOptions(final boolean empty) {
/**
* SdkVersion object that contains the Sentry Client Name and its version. This object is only
* applied to {@link SentryReplayEvent}s.
*/
private @Nullable SdkVersion sdkVersion;

public SentryReplayOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) {
if (!empty) {
setMaskAllText(true);
setMaskAllImages(true);
Expand All @@ -123,14 +130,18 @@ public SentryReplayOptions(final boolean empty) {
maskViewClasses.add(ANDROIDX_MEDIA_VIEW_CLASS_NAME);
maskViewClasses.add(EXOPLAYER_CLASS_NAME);
maskViewClasses.add(EXOPLAYER_STYLED_CLASS_NAME);
this.sdkVersion = sdkVersion;
}
}

public SentryReplayOptions(
final @Nullable Double sessionSampleRate, final @Nullable Double onErrorSampleRate) {
this(false);
final @Nullable Double sessionSampleRate,
final @Nullable Double onErrorSampleRate,
final @Nullable SdkVersion sdkVersion) {
this(false, sdkVersion);
this.sessionSampleRate = sessionSampleRate;
this.onErrorSampleRate = onErrorSampleRate;
this.sdkVersion = sdkVersion;
}

@Nullable
Expand Down Expand Up @@ -282,4 +293,14 @@ public boolean isTrackOrientationChange() {
public void setTrackOrientationChange(final boolean trackOrientationChange) {
this.trackOrientationChange = trackOrientationChange;
}

@ApiStatus.Internal
public @Nullable SdkVersion getSdkVersion() {
return sdkVersion;
}

@ApiStatus.Internal
public void setSdkVersion(final @Nullable SdkVersion sdkVersion) {
this.sdkVersion = sdkVersion;
}
}
14 changes: 13 additions & 1 deletion sentry/src/test/java/io/sentry/MainEventProcessorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import kotlin.test.assertTrue

class MainEventProcessorTest {
class Fixture {
private val sentryOptions: SentryOptions = SentryOptions().apply {
val sentryOptions: SentryOptions = SentryOptions().apply {
dsn = dsnString
release = "release"
dist = "dist"
Expand Down Expand Up @@ -619,6 +619,18 @@ class MainEventProcessorTest {
assertEquals("value1", replayEvent.tags!!["tag1"])
}

@Test
fun `uses SdkVersion from replay options for replay events`() {
val sut = fixture.getSut(tags = mapOf("tag1" to "value1"))

fixture.sentryOptions.experimental.sessionReplay.sdkVersion = SdkVersion("dart", "3.2.1")
var replayEvent = SentryReplayEvent()
replayEvent = sut.process(replayEvent, Hint())

assertEquals("3.2.1", replayEvent.sdk!!.version)
assertEquals("dart", replayEvent.sdk!!.name)
}

private fun generateCrashedEvent(crashedThread: Thread = Thread.currentThread()) =
SentryEvent().apply {
val mockThrowable = mock<Throwable>()
Expand Down
6 changes: 3 additions & 3 deletions sentry/src/test/java/io/sentry/SentryReplayOptionsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class SentryReplayOptionsTest {

@Test
fun `uses medium quality as default`() {
val replayOptions = SentryReplayOptions(true)
val replayOptions = SentryReplayOptions(true, null)

assertEquals(SentryReplayOptions.SentryReplayQuality.MEDIUM, replayOptions.quality)
assertEquals(75_000, replayOptions.quality.bitRate)
Expand All @@ -16,15 +16,15 @@ class SentryReplayOptionsTest {

@Test
fun `low quality`() {
val replayOptions = SentryReplayOptions(true).apply { quality = SentryReplayOptions.SentryReplayQuality.LOW }
val replayOptions = SentryReplayOptions(true, null).apply { quality = SentryReplayOptions.SentryReplayQuality.LOW }

assertEquals(50_000, replayOptions.quality.bitRate)
assertEquals(0.8f, replayOptions.quality.sizeScale)
}

@Test
fun `high quality`() {
val replayOptions = SentryReplayOptions(true).apply { quality = SentryReplayOptions.SentryReplayQuality.HIGH }
val replayOptions = SentryReplayOptions(true, null).apply { quality = SentryReplayOptions.SentryReplayQuality.HIGH }

assertEquals(100_000, replayOptions.quality.bitRate)
assertEquals(1.0f, replayOptions.quality.sizeScale)
Expand Down
Loading