Skip to content

Commit e70d583

Browse files
authored
added SentryOptions.continuousProfilesSampleRate (#4013)
* added SentryOptions.continuousProfilesSampleRate * now continuous profiling is disabled if continuousProfilesSampleRate is 0 * profiles directory is created when continuous profiling is enabled, too * continuous profiling decision is passed to SentryAppStartProfilingOptions * app start continuous profiling is sampled, too
1 parent b0e8333 commit e70d583

19 files changed

+354
-17
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ final class ManifestMetadataReader {
6464

6565
static final String PROFILES_SAMPLE_RATE = "io.sentry.traces.profiling.sample-rate";
6666

67+
static final String CONTINUOUS_PROFILES_SAMPLE_RATE =
68+
"io.sentry.traces.profiling.continuous-sample-rate";
69+
6770
@ApiStatus.Experimental static final String TRACE_SAMPLING = "io.sentry.traces.trace-sampling";
6871
static final String TRACE_PROPAGATION_TARGETS = "io.sentry.traces.trace-propagation-targets";
6972

@@ -315,6 +318,14 @@ static void applyMetadata(
315318
}
316319
}
317320

321+
if (options.getContinuousProfilesSampleRate() == 1.0) {
322+
final double continuousProfilesSampleRate =
323+
readDouble(metadata, logger, CONTINUOUS_PROFILES_SAMPLE_RATE);
324+
if (continuousProfilesSampleRate != -1) {
325+
options.setContinuousProfilesSampleRate(continuousProfilesSampleRate);
326+
}
327+
}
328+
318329
options.setEnableUserInteractionTracing(
319330
readBool(metadata, logger, TRACES_UI_ENABLE, options.isEnableUserInteractionTracing()));
320331

sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ private void createAndStartContinuousProfiler(
162162
final @NotNull Context context,
163163
final @NotNull SentryAppStartProfilingOptions profilingOptions,
164164
final @NotNull AppStartMetrics appStartMetrics) {
165+
166+
if (!profilingOptions.isContinuousProfileSampled()) {
167+
logger.log(SentryLevel.DEBUG, "App start profiling was not sampled. It will not start.");
168+
return;
169+
}
170+
165171
final @NotNull IContinuousProfiler appStartContinuousProfiler =
166172
new AndroidContinuousProfiler(
167173
buildInfoProvider,

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,47 @@ class ManifestMetadataReaderTest {
800800
assertNull(fixture.options.profilesSampleRate)
801801
}
802802

803+
@Test
804+
fun `applyMetadata reads continuousProfilesSampleRate from metadata`() {
805+
// Arrange
806+
val expectedSampleRate = 0.99f
807+
val bundle = bundleOf(ManifestMetadataReader.CONTINUOUS_PROFILES_SAMPLE_RATE to expectedSampleRate)
808+
val context = fixture.getContext(metaData = bundle)
809+
810+
// Act
811+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
812+
813+
// Assert
814+
assertEquals(expectedSampleRate.toDouble(), fixture.options.continuousProfilesSampleRate)
815+
}
816+
817+
@Test
818+
fun `applyMetadata does not override continuousProfilesSampleRate from options`() {
819+
// Arrange
820+
val expectedSampleRate = 0.99f
821+
fixture.options.continuousProfilesSampleRate = expectedSampleRate.toDouble()
822+
val bundle = bundleOf(ManifestMetadataReader.CONTINUOUS_PROFILES_SAMPLE_RATE to 0.1f)
823+
val context = fixture.getContext(metaData = bundle)
824+
825+
// Act
826+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
827+
828+
// Assert
829+
assertEquals(expectedSampleRate.toDouble(), fixture.options.continuousProfilesSampleRate)
830+
}
831+
832+
@Test
833+
fun `applyMetadata without specifying continuousProfilesSampleRate, stays 1`() {
834+
// Arrange
835+
val context = fixture.getContext()
836+
837+
// Act
838+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
839+
840+
// Assert
841+
assertEquals(1.0, fixture.options.continuousProfilesSampleRate)
842+
}
843+
803844
@Test
804845
fun `applyMetadata reads tracePropagationTargets to options`() {
805846
// Arrange

sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ class SentryPerformanceProviderTest {
259259
)
260260
}
261261

262+
@Test
263+
fun `when continuous profile is not sampled, continuous profiler is not started`() {
264+
fixture.getSut { config ->
265+
writeConfig(config, continuousProfileSampled = false)
266+
}
267+
assertNull(AppStartMetrics.getInstance().appStartProfiler)
268+
assertNull(AppStartMetrics.getInstance().appStartContinuousProfiler)
269+
verify(fixture.logger).log(
270+
eq(SentryLevel.DEBUG),
271+
eq("App start profiling was not sampled. It will not start.")
272+
)
273+
}
274+
262275
// This case should never happen in reality, but it's technically possible to have such configuration
263276
@Test
264277
fun `when both transaction and continuous profilers are enabled, only continuous profiler is created`() {
@@ -331,6 +344,7 @@ class SentryPerformanceProviderTest {
331344
traceSampleRate: Double = 1.0,
332345
profileSampled: Boolean = true,
333346
profileSampleRate: Double = 1.0,
347+
continuousProfileSampled: Boolean = true,
334348
profilingTracesDirPath: String = traceDir.absolutePath
335349
) {
336350
val appStartProfilingOptions = SentryAppStartProfilingOptions()
@@ -340,6 +354,7 @@ class SentryPerformanceProviderTest {
340354
appStartProfilingOptions.traceSampleRate = traceSampleRate
341355
appStartProfilingOptions.isProfileSampled = profileSampled
342356
appStartProfilingOptions.profileSampleRate = profileSampleRate
357+
appStartProfilingOptions.isContinuousProfileSampled = continuousProfileSampled
343358
appStartProfilingOptions.profilingTracesDirPath = profilingTracesDirPath
344359
appStartProfilingOptions.profilingTracesHz = 101
345360
JsonSerializer(SentryOptions.empty()).serialize(appStartProfilingOptions, FileWriter(configFile))

sentry/api/sentry.api

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ public final class io/sentry/ExternalOptions {
458458
public static fun from (Lio/sentry/config/PropertiesProvider;Lio/sentry/ILogger;)Lio/sentry/ExternalOptions;
459459
public fun getBundleIds ()Ljava/util/Set;
460460
public fun getContextTags ()Ljava/util/List;
461+
public fun getContinuousProfilesSampleRate ()Ljava/lang/Double;
461462
public fun getCron ()Lio/sentry/SentryOptions$Cron;
462463
public fun getDebug ()Ljava/lang/Boolean;
463464
public fun getDist ()Ljava/lang/String;
@@ -491,6 +492,7 @@ public final class io/sentry/ExternalOptions {
491492
public fun isGlobalHubMode ()Ljava/lang/Boolean;
492493
public fun isSendDefaultPii ()Ljava/lang/Boolean;
493494
public fun isSendModules ()Ljava/lang/Boolean;
495+
public fun setContinuousProfilesSampleRate (Ljava/lang/Double;)V
494496
public fun setCron (Lio/sentry/SentryOptions$Cron;)V
495497
public fun setDebug (Ljava/lang/Boolean;)V
496498
public fun setDist (Ljava/lang/String;)V
@@ -2488,11 +2490,13 @@ public final class io/sentry/SentryAppStartProfilingOptions : io/sentry/JsonSeri
24882490
public fun getProfilingTracesHz ()I
24892491
public fun getTraceSampleRate ()Ljava/lang/Double;
24902492
public fun getUnknown ()Ljava/util/Map;
2493+
public fun isContinuousProfileSampled ()Z
24912494
public fun isContinuousProfilingEnabled ()Z
24922495
public fun isProfileSampled ()Z
24932496
public fun isProfilingEnabled ()Z
24942497
public fun isTraceSampled ()Z
24952498
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
2499+
public fun setContinuousProfileSampled (Z)V
24962500
public fun setContinuousProfilingEnabled (Z)V
24972501
public fun setProfileSampleRate (Ljava/lang/Double;)V
24982502
public fun setProfileSampled (Z)V
@@ -2511,6 +2515,7 @@ public final class io/sentry/SentryAppStartProfilingOptions$Deserializer : io/se
25112515
}
25122516

25132517
public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys {
2518+
public static final field CONTINUOUS_PROFILE_SAMPLED Ljava/lang/String;
25142519
public static final field IS_CONTINUOUS_PROFILING_ENABLED Ljava/lang/String;
25152520
public static final field IS_PROFILING_ENABLED Ljava/lang/String;
25162521
public static final field PROFILE_SAMPLED Ljava/lang/String;
@@ -2944,6 +2949,7 @@ public class io/sentry/SentryOptions {
29442949
public fun getConnectionTimeoutMillis ()I
29452950
public fun getContextTags ()Ljava/util/List;
29462951
public fun getContinuousProfiler ()Lio/sentry/IContinuousProfiler;
2952+
public fun getContinuousProfilesSampleRate ()D
29472953
public fun getCron ()Lio/sentry/SentryOptions$Cron;
29482954
public fun getDateProvider ()Lio/sentry/SentryDateProvider;
29492955
public fun getDebugMetaLoader ()Lio/sentry/internal/debugmeta/IDebugMetaLoader;
@@ -3060,6 +3066,7 @@ public class io/sentry/SentryOptions {
30603066
public fun setConnectionStatusProvider (Lio/sentry/IConnectionStatusProvider;)V
30613067
public fun setConnectionTimeoutMillis (I)V
30623068
public fun setContinuousProfiler (Lio/sentry/IContinuousProfiler;)V
3069+
public fun setContinuousProfilesSampleRate (D)V
30633070
public fun setCron (Lio/sentry/SentryOptions$Cron;)V
30643071
public fun setDateProvider (Lio/sentry/SentryDateProvider;)V
30653072
public fun setDebug (Z)V
@@ -3759,6 +3766,7 @@ public final class io/sentry/TraceContext$JsonKeys {
37593766
public final class io/sentry/TracesSampler {
37603767
public fun <init> (Lio/sentry/SentryOptions;)V
37613768
public fun sample (Lio/sentry/SamplingContext;)Lio/sentry/TracesSamplingDecision;
3769+
public fun sampleContinuousProfile ()Z
37623770
}
37633771

37643772
public final class io/sentry/TracesSamplingDecision {
@@ -6316,6 +6324,7 @@ public final class io/sentry/util/Random : java/io/Serializable {
63166324

63176325
public final class io/sentry/util/SampleRateUtils {
63186326
public fun <init> ()V
6327+
public static fun isValidContinuousProfilesSampleRate (D)Z
63196328
public static fun isValidProfilesSampleRate (Ljava/lang/Double;)Z
63206329
public static fun isValidSampleRate (Ljava/lang/Double;)Z
63216330
public static fun isValidTracesSampleRate (Ljava/lang/Double;)Z

sentry/src/main/java/io/sentry/ExternalOptions.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public final class ExternalOptions {
2828
private @Nullable Boolean enableDeduplication;
2929
private @Nullable Double tracesSampleRate;
3030
private @Nullable Double profilesSampleRate;
31+
private @Nullable Double continuousProfilesSampleRate;
3132
private @Nullable SentryOptions.RequestSize maxRequestBodySize;
3233
private final @NotNull Map<String, @NotNull String> tags = new ConcurrentHashMap<>();
3334
private @Nullable SentryOptions.Proxy proxy;
@@ -73,6 +74,8 @@ public final class ExternalOptions {
7374
propertiesProvider.getBooleanProperty("uncaught.handler.print-stacktrace"));
7475
options.setTracesSampleRate(propertiesProvider.getDoubleProperty("traces-sample-rate"));
7576
options.setProfilesSampleRate(propertiesProvider.getDoubleProperty("profiles-sample-rate"));
77+
options.setContinuousProfilesSampleRate(
78+
propertiesProvider.getDoubleProperty("continuous-profiles-sample-rate"));
7679
options.setDebug(propertiesProvider.getBooleanProperty("debug"));
7780
options.setEnableDeduplication(propertiesProvider.getBooleanProperty("enable-deduplication"));
7881
options.setSendClientReports(propertiesProvider.getBooleanProperty("send-client-reports"));
@@ -284,6 +287,14 @@ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) {
284287
this.profilesSampleRate = profilesSampleRate;
285288
}
286289

290+
public @Nullable Double getContinuousProfilesSampleRate() {
291+
return continuousProfilesSampleRate;
292+
}
293+
294+
public void setContinuousProfilesSampleRate(final @Nullable Double continuousProfilesSampleRate) {
295+
this.continuousProfilesSampleRate = continuousProfilesSampleRate;
296+
}
297+
287298
public @Nullable SentryOptions.RequestSize getMaxRequestBodySize() {
288299
return maxRequestBodySize;
289300
}

sentry/src/main/java/io/sentry/Scopes.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -926,9 +926,15 @@ public void flush(long timeoutMillis) {
926926
@Override
927927
public void startProfiler() {
928928
if (getOptions().isContinuousProfilingEnabled()) {
929-
getOptions().getLogger().log(SentryLevel.DEBUG, "Started continuous Profiling.");
930-
getOptions().getContinuousProfiler().start();
931-
} else {
929+
if (getOptions().getInternalTracesSampler().sampleContinuousProfile()) {
930+
getOptions().getLogger().log(SentryLevel.DEBUG, "Started continuous Profiling.");
931+
getOptions().getContinuousProfiler().start();
932+
} else {
933+
getOptions()
934+
.getLogger()
935+
.log(SentryLevel.DEBUG, "Profiler was not started due to sampling decision.");
936+
}
937+
} else if (getOptions().isProfilingEnabled()) {
932938
getOptions()
933939
.getLogger()
934940
.log(

sentry/src/main/java/io/sentry/Sentry.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,8 @@ private static void initConfigurations(final @NotNull SentryOptions options) {
514514
}
515515

516516
final String profilingTracesDirPath = options.getProfilingTracesDirPath();
517-
if (options.isProfilingEnabled() && profilingTracesDirPath != null) {
517+
if ((options.isProfilingEnabled() || options.isContinuousProfilingEnabled())
518+
&& profilingTracesDirPath != null) {
518519

519520
final File profilingTracesDir = new File(profilingTracesDirPath);
520521
profilingTracesDir.mkdirs();

sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public final class SentryAppStartProfilingOptions implements JsonUnknown, JsonSe
2020
boolean isProfilingEnabled;
2121
boolean isContinuousProfilingEnabled;
2222
int profilingTracesHz;
23+
boolean continuousProfileSampled;
2324

2425
private @Nullable Map<String, Object> unknown;
2526

@@ -29,6 +30,7 @@ public SentryAppStartProfilingOptions() {
2930
traceSampleRate = null;
3031
profileSampled = false;
3132
profileSampleRate = null;
33+
continuousProfileSampled = false;
3234
profilingTracesDirPath = null;
3335
isProfilingEnabled = false;
3436
isContinuousProfilingEnabled = false;
@@ -42,6 +44,7 @@ public SentryAppStartProfilingOptions() {
4244
traceSampleRate = samplingDecision.getSampleRate();
4345
profileSampled = samplingDecision.getProfileSampled();
4446
profileSampleRate = samplingDecision.getProfileSampleRate();
47+
continuousProfileSampled = options.getInternalTracesSampler().sampleContinuousProfile();
4548
profilingTracesDirPath = options.getProfilingTracesDirPath();
4649
isProfilingEnabled = options.isProfilingEnabled();
4750
isContinuousProfilingEnabled = options.isContinuousProfilingEnabled();
@@ -56,6 +59,14 @@ public boolean isProfileSampled() {
5659
return profileSampled;
5760
}
5861

62+
public void setContinuousProfileSampled(boolean continuousProfileSampled) {
63+
this.continuousProfileSampled = continuousProfileSampled;
64+
}
65+
66+
public boolean isContinuousProfileSampled() {
67+
return continuousProfileSampled;
68+
}
69+
5970
public void setProfileSampleRate(final @Nullable Double profileSampleRate) {
6071
this.profileSampleRate = profileSampleRate;
6172
}
@@ -117,6 +128,7 @@ public int getProfilingTracesHz() {
117128
public static final class JsonKeys {
118129
public static final String PROFILE_SAMPLED = "profile_sampled";
119130
public static final String PROFILE_SAMPLE_RATE = "profile_sample_rate";
131+
public static final String CONTINUOUS_PROFILE_SAMPLED = "continuous_profile_sampled";
120132
public static final String TRACE_SAMPLED = "trace_sampled";
121133
public static final String TRACE_SAMPLE_RATE = "trace_sample_rate";
122134
public static final String PROFILING_TRACES_DIR_PATH = "profiling_traces_dir_path";
@@ -131,6 +143,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
131143
writer.beginObject();
132144
writer.name(JsonKeys.PROFILE_SAMPLED).value(logger, profileSampled);
133145
writer.name(JsonKeys.PROFILE_SAMPLE_RATE).value(logger, profileSampleRate);
146+
writer.name(JsonKeys.CONTINUOUS_PROFILE_SAMPLED).value(logger, continuousProfileSampled);
134147
writer.name(JsonKeys.TRACE_SAMPLED).value(logger, traceSampled);
135148
writer.name(JsonKeys.TRACE_SAMPLE_RATE).value(logger, traceSampleRate);
136149
writer.name(JsonKeys.PROFILING_TRACES_DIR_PATH).value(logger, profilingTracesDirPath);
@@ -186,6 +199,12 @@ public static final class Deserializer
186199
options.profileSampleRate = profileSampleRate;
187200
}
188201
break;
202+
case JsonKeys.CONTINUOUS_PROFILE_SAMPLED:
203+
Boolean continuousProfileSampled = reader.nextBooleanOrNull();
204+
if (continuousProfileSampled != null) {
205+
options.continuousProfileSampled = continuousProfileSampled;
206+
}
207+
break;
189208
case JsonKeys.TRACE_SAMPLED:
190209
Boolean traceSampled = reader.nextBooleanOrNull();
191210
if (traceSampled != null) {

0 commit comments

Comments
 (0)