Skip to content

Commit edd22f0

Browse files
runningcodeclaude
andcommitted
ref(checkin): Measure check-in durations with Stopwatch (JAVA-576)
All four check-in paths kept a `long startTime = System.nanoTime()` and subtracted it in a finally block: CheckInUtils and the SentryCheckInAdvice in sentry-spring, sentry-spring-jakarta and sentry-spring-7. The serialized duration is unchanged, to the bit. JavaUptimeClock.tickNanos() is System.nanoTime(), and check-ins have no Android path where the two could diverge, so this is the same arithmetic behind a name. That is the point of naming the guarantee rather than the mechanism: it makes a conversion that touches a customer-facing value provably inert, and therefore landable before the major. Each site carries a TODO [MAJOR] for the change that is not inert: on elapsed-real-time, a cron job that spans device sleep would report the duration a user would measure rather than the CPU time it had. The clock is resolved as JavaUptimeClock.getInstance() rather than through options, so an uninitialised SDK reaches a working clock without a scopes lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 0a21879 commit edd22f0

4 files changed

Lines changed: 24 additions & 8 deletions

File tree

sentry-spring-7/src/main/java/io/sentry/spring7/checkin/SentryCheckInAdvice.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.sentry.ScopesAdapter;
1010
import io.sentry.SentryLevel;
1111
import io.sentry.protocol.SentryId;
12+
import io.sentry.time.JavaUptimeClock;
13+
import io.sentry.time.Stopwatch;
1214
import io.sentry.util.Objects;
1315
import io.sentry.util.TracingUtils;
1416
import java.lang.reflect.Method;
@@ -91,7 +93,9 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
9193
TracingUtils.startNewTrace(scopes);
9294

9395
@Nullable SentryId checkInId = null;
94-
final long startTime = System.nanoTime();
96+
// TODO [MAJOR]: switch to ElapsedRealtimeClock, so that a job spanning device sleep
97+
// reports the duration a user would measure rather than the CPU time it had.
98+
final @NotNull Stopwatch stopwatch = Stopwatch.started(JavaUptimeClock.getInstance());
9599
boolean didError = false;
96100

97101
try {
@@ -105,7 +109,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
105109
} finally {
106110
final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK;
107111
CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status);
108-
checkIn.setDuration(DateUtils.nanosToSeconds(System.nanoTime() - startTime));
112+
checkIn.setDuration(DateUtils.nanosToSeconds(stopwatch.elapsedNanos()));
109113
scopes.captureCheckIn(checkIn);
110114
}
111115
}

sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.sentry.ScopesAdapter;
1010
import io.sentry.SentryLevel;
1111
import io.sentry.protocol.SentryId;
12+
import io.sentry.time.JavaUptimeClock;
13+
import io.sentry.time.Stopwatch;
1214
import io.sentry.util.Objects;
1315
import io.sentry.util.TracingUtils;
1416
import java.lang.reflect.Method;
@@ -91,7 +93,9 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
9193
TracingUtils.startNewTrace(scopes);
9294

9395
@Nullable SentryId checkInId = null;
94-
final long startTime = System.nanoTime();
96+
// TODO [MAJOR]: switch to ElapsedRealtimeClock, so that a job spanning device sleep
97+
// reports the duration a user would measure rather than the CPU time it had.
98+
final @NotNull Stopwatch stopwatch = Stopwatch.started(JavaUptimeClock.getInstance());
9599
boolean didError = false;
96100

97101
try {
@@ -105,7 +109,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
105109
} finally {
106110
final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK;
107111
CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status);
108-
checkIn.setDuration(DateUtils.nanosToSeconds(System.nanoTime() - startTime));
112+
checkIn.setDuration(DateUtils.nanosToSeconds(stopwatch.elapsedNanos()));
109113
scopes.captureCheckIn(checkIn);
110114
}
111115
}

sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.sentry.ScopesAdapter;
1010
import io.sentry.SentryLevel;
1111
import io.sentry.protocol.SentryId;
12+
import io.sentry.time.JavaUptimeClock;
13+
import io.sentry.time.Stopwatch;
1214
import io.sentry.util.Objects;
1315
import io.sentry.util.TracingUtils;
1416
import java.lang.reflect.Method;
@@ -94,7 +96,9 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
9496
TracingUtils.startNewTrace(scopes);
9597

9698
@Nullable SentryId checkInId = null;
97-
final long startTime = System.nanoTime();
99+
// TODO [MAJOR]: switch to ElapsedRealtimeClock, so that a job spanning device sleep
100+
// reports the duration a user would measure rather than the CPU time it had.
101+
final @NotNull Stopwatch stopwatch = Stopwatch.started(JavaUptimeClock.getInstance());
98102
boolean didError = false;
99103

100104
try {
@@ -108,7 +112,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl
108112
} finally {
109113
final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK;
110114
CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status);
111-
checkIn.setDuration(DateUtils.nanosToSeconds(System.nanoTime() - startTime));
115+
checkIn.setDuration(DateUtils.nanosToSeconds(stopwatch.elapsedNanos()));
112116
scopes.captureCheckIn(checkIn);
113117
}
114118
}

sentry/src/main/java/io/sentry/util/CheckInUtils.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.sentry.MonitorConfig;
1010
import io.sentry.Sentry;
1111
import io.sentry.protocol.SentryId;
12+
import io.sentry.time.JavaUptimeClock;
13+
import io.sentry.time.Stopwatch;
1214
import java.util.List;
1315
import java.util.concurrent.Callable;
1416
import org.jetbrains.annotations.ApiStatus;
@@ -37,7 +39,9 @@ public static <U> U withCheckIn(
3739
try (final @NotNull ISentryLifecycleToken ignored =
3840
Sentry.forkedScopes("CheckInUtils").makeCurrent()) {
3941
final @NotNull IScopes scopes = Sentry.getCurrentScopes();
40-
final long startTime = System.nanoTime();
42+
// TODO [MAJOR]: switch to ElapsedRealtimeClock, so that a job spanning device sleep
43+
// reports the duration a user would measure rather than the CPU time it had.
44+
final @NotNull Stopwatch stopwatch = Stopwatch.started(JavaUptimeClock.getInstance());
4145
boolean didError = false;
4246

4347
TracingUtils.startNewTrace(scopes);
@@ -61,7 +65,7 @@ public static <U> U withCheckIn(
6165
if (environment != null) {
6266
checkIn.setEnvironment(environment);
6367
}
64-
checkIn.setDuration(DateUtils.nanosToSeconds(System.nanoTime() - startTime));
68+
checkIn.setDuration(DateUtils.nanosToSeconds(stopwatch.elapsedNanos()));
6569
scopes.captureCheckIn(checkIn);
6670
}
6771
}

0 commit comments

Comments
 (0)