Skip to content

Commit d7ac417

Browse files
buenaflorclaude
andcommitted
feat: Add IAppStartExtender bridge and SentryOptions wiring for app start extension
Introduces the @ApiStatus.Internal IAppStartExtender contract and a NoOp default, wired into SentryOptions. This is the naming-stable core bridge for the app start extension API; it is inert until the Android implementation (PR2/PR3) and public Sentry facade (PR4) land. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0c118e9 commit d7ac417

6 files changed

Lines changed: 137 additions & 0 deletions

File tree

sentry/api/sentry.api

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,12 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub {
796796
public fun withScope (Lio/sentry/ScopeCallback;)V
797797
}
798798

799+
public abstract interface class io/sentry/IAppStartExtender {
800+
public abstract fun extendAppStart ()V
801+
public abstract fun finishAppStart ()V
802+
public abstract fun getExtendedAppStartSpan ()Lio/sentry/ISpan;
803+
}
804+
799805
public abstract interface class io/sentry/IConnectionStatusProvider : java/io/Closeable {
800806
public abstract fun addConnectionStatusObserver (Lio/sentry/IConnectionStatusProvider$IConnectionStatusObserver;)Z
801807
public abstract fun getConnectionStatus ()Lio/sentry/IConnectionStatusProvider$ConnectionStatus;
@@ -1543,6 +1549,13 @@ public final class io/sentry/MonitorScheduleUnit : java/lang/Enum {
15431549
public static fun values ()[Lio/sentry/MonitorScheduleUnit;
15441550
}
15451551

1552+
public final class io/sentry/NoOpAppStartExtender : io/sentry/IAppStartExtender {
1553+
public fun extendAppStart ()V
1554+
public fun finishAppStart ()V
1555+
public fun getExtendedAppStartSpan ()Lio/sentry/ISpan;
1556+
public static fun getInstance ()Lio/sentry/NoOpAppStartExtender;
1557+
}
1558+
15461559
public final class io/sentry/NoOpCompositePerformanceCollector : io/sentry/CompositePerformanceCollector {
15471560
public fun close ()V
15481561
public static fun getInstance ()Lio/sentry/NoOpCompositePerformanceCollector;
@@ -3603,6 +3616,7 @@ public class io/sentry/SentryOptions {
36033616
public fun addScopeObserver (Lio/sentry/IScopeObserver;)V
36043617
public static fun empty ()Lio/sentry/SentryOptions;
36053618
public fun findPersistingScopeObserver ()Lio/sentry/cache/PersistingScopeObserver;
3619+
public fun getAppStartExtender ()Lio/sentry/IAppStartExtender;
36063620
public fun getBackpressureMonitor ()Lio/sentry/backpressure/IBackpressureMonitor;
36073621
public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;
36083622
public fun getBeforeEnvelopeCallback ()Lio/sentry/SentryOptions$BeforeEnvelopeCallback;
@@ -3749,6 +3763,7 @@ public class io/sentry/SentryOptions {
37493763
public fun isTraceSampling ()Z
37503764
public fun isTracingEnabled ()Z
37513765
public fun merge (Lio/sentry/ExternalOptions;)V
3766+
public fun setAppStartExtender (Lio/sentry/IAppStartExtender;)V
37523767
public fun setAttachServerName (Z)V
37533768
public fun setAttachStacktrace (Z)V
37543769
public fun setAttachThreads (Z)V
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.sentry;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
/**
7+
* Bridges the {@code Sentry.extendAppStart()} / {@code Sentry.finishAppStart()} / {@code
8+
* Sentry.getExtendedAppStartSpan()} static API to the Android implementation. The default
9+
* implementation ({@link NoOpAppStartExtender}) does nothing, so the API is a no-op on platforms
10+
* that don't provide an app start measurement.
11+
*/
12+
@ApiStatus.Internal
13+
public interface IAppStartExtender {
14+
15+
/** Starts extending the app start measurement. See {@code Sentry.extendAppStart()}. */
16+
void extendAppStart();
17+
18+
/** Finishes the extended app start. See {@code Sentry.finishAppStart()}. */
19+
void finishAppStart();
20+
21+
/**
22+
* Returns the active extended app start span to attach child spans to, or a no-op span when no
23+
* extension is active. See {@code Sentry.getExtendedAppStartSpan()}.
24+
*/
25+
@NotNull
26+
ISpan getExtendedAppStartSpan();
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.sentry;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
public final class NoOpAppStartExtender implements IAppStartExtender {
6+
7+
private static final @NotNull NoOpAppStartExtender instance = new NoOpAppStartExtender();
8+
9+
private NoOpAppStartExtender() {}
10+
11+
public static @NotNull NoOpAppStartExtender getInstance() {
12+
return instance;
13+
}
14+
15+
@Override
16+
public void extendAppStart() {
17+
// no-op
18+
}
19+
20+
@Override
21+
public void finishAppStart() {
22+
// no-op
23+
}
24+
25+
@Override
26+
public @NotNull ISpan getExtendedAppStartSpan() {
27+
return NoOpSpan.getInstance();
28+
}
29+
}

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ public class SentryOptions {
529529
private @NotNull FullyDisplayedReporter fullyDisplayedReporter =
530530
FullyDisplayedReporter.getInstance();
531531

532+
/** Bridges the app start extension API to the Android implementation. */
533+
private @NotNull IAppStartExtender appStartExtender = NoOpAppStartExtender.getInstance();
534+
532535
private @NotNull IConnectionStatusProvider connectionStatusProvider =
533536
new NoOpConnectionStatusProvider();
534537

@@ -2653,6 +2656,21 @@ public void setFullyDisplayedReporter(
26532656
this.fullyDisplayedReporter = fullyDisplayedReporter;
26542657
}
26552658

2659+
/**
2660+
* Gets the app start extender, which bridges the app start extension API to its implementation.
2661+
*
2662+
* @return the app start extender.
2663+
*/
2664+
@ApiStatus.Internal
2665+
public @NotNull IAppStartExtender getAppStartExtender() {
2666+
return appStartExtender;
2667+
}
2668+
2669+
@ApiStatus.Internal
2670+
public void setAppStartExtender(final @NotNull IAppStartExtender appStartExtender) {
2671+
this.appStartExtender = appStartExtender;
2672+
}
2673+
26562674
/**
26572675
* Whether OPTIONS requests should be traced.
26582676
*
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.sentry
2+
3+
import kotlin.test.Test
4+
import kotlin.test.assertSame
5+
6+
class NoOpAppStartExtenderTest {
7+
private val extender = NoOpAppStartExtender.getInstance()
8+
9+
@Test
10+
fun `getInstance returns the same singleton`() {
11+
assertSame(NoOpAppStartExtender.getInstance(), NoOpAppStartExtender.getInstance())
12+
}
13+
14+
@Test
15+
fun `getExtendedAppStartSpan returns NoOpSpan`() {
16+
assertSame(NoOpSpan.getInstance(), extender.extendedAppStartSpan)
17+
}
18+
19+
@Test
20+
fun `extendAppStart is a no-op`() {
21+
extender.extendAppStart()
22+
}
23+
24+
@Test
25+
fun `finishAppStart is a no-op`() {
26+
extender.finishAppStart()
27+
}
28+
}

sentry/src/test/java/io/sentry/SentryOptionsTest.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,26 @@ class SentryOptionsTest {
645645
assertEquals(FullyDisplayedReporter.getInstance(), SentryOptions().fullyDisplayedReporter)
646646
}
647647

648+
@Test
649+
fun `when options are initialized, appStartExtender defaults to noop`() {
650+
assertEquals(NoOpAppStartExtender.getInstance(), SentryOptions().appStartExtender)
651+
}
652+
653+
@Test
654+
fun `when appStartExtender is set, its returned as well`() {
655+
val options = SentryOptions()
656+
val customExtender =
657+
object : IAppStartExtender {
658+
override fun extendAppStart() = Unit
659+
660+
override fun finishAppStart() = Unit
661+
662+
override fun getExtendedAppStartSpan(): ISpan = NoOpSpan.getInstance()
663+
}
664+
options.appStartExtender = customExtender
665+
assertEquals(customExtender, options.appStartExtender)
666+
}
667+
648668
@Test
649669
fun `when options are initialized, connectionStatusProvider is not null and default to noop`() {
650670
assertNotNull(SentryOptions().connectionStatusProvider)

0 commit comments

Comments
 (0)