Skip to content

Commit 68ca0a1

Browse files
committed
Merge branch 'feat/unhandled-sessions-cache' into feat/unhandled-sessions-internal-api
2 parents 8b93923 + 81822da commit 68ca0a1

7 files changed

Lines changed: 109 additions & 90 deletions

File tree

‎sentry/api/sentry.api‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4290,12 +4290,12 @@ public final class io/sentry/Session : io/sentry/JsonSerializable, io/sentry/Jso
42904290
public fun getTimestamp ()Ljava/util/Date;
42914291
public fun getUnknown ()Ljava/util/Map;
42924292
public fun getUserAgent ()Ljava/lang/String;
4293-
public fun isPendingUnhandled ()Z
4293+
public fun hasNonTerminatingUnhandledError ()Z
42944294
public fun isTerminated ()Z
4295-
public fun markPendingUnhandled ()Z
4295+
public fun recordNonTerminatingUnhandledError ()Z
42964296
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V
42974297
public fun setInitAsTrue ()V
4298-
public fun setPendingUnhandled (Z)V
4298+
public fun setNonTerminatingUnhandledError (Z)V
42994299
public fun setUnknown (Ljava/util/Map;)V
43004300
public fun update (Lio/sentry/Session$State;Ljava/lang/String;Z)Z
43014301
public fun update (Lio/sentry/Session$State;Ljava/lang/String;ZLjava/lang/String;)Z
@@ -4316,7 +4316,7 @@ public final class io/sentry/Session$JsonKeys {
43164316
public static final field ERRORS Ljava/lang/String;
43174317
public static final field INIT Ljava/lang/String;
43184318
public static final field IP_ADDRESS Ljava/lang/String;
4319-
public static final field PENDING_UNHANDLED Ljava/lang/String;
4319+
public static final field NON_TERMINATING_UNHANDLED_ERROR Ljava/lang/String;
43204320
public static final field RELEASE Ljava/lang/String;
43214321
public static final field SEQ Ljava/lang/String;
43224322
public static final field SID Ljava/lang/String;

‎sentry/src/main/java/io/sentry/Session.java‎

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,15 @@ public enum State {
6868
private @Nullable String abnormalMechanism;
6969

7070
/**
71-
* Whether the session experienced an unhandled (but non-terminal) exception. Kept locally and
72-
* persisted with the session, but never sent as a status while the session is alive. On end() the
73-
* session is finalized as {@link State#Unhandled} instead of {@link State#Exited} unless a crash
74-
* escalated it to {@link State#Crashed}.
71+
* Whether the session experienced an unhandled error that did <em>not</em> terminate the process,
72+
* e.g. an unhandled Flutter exception. A native crash is also unhandled, but it kills the process
73+
* and therefore ends the session as {@link State#Crashed} instead.
74+
*
75+
* <p>Kept locally and persisted with the session, but never sent as a status while the session is
76+
* alive. On end() the session is finalized as {@link State#Unhandled} instead of {@link
77+
* State#Exited}, unless a crash escalated it to {@link State#Crashed}.
7578
*/
76-
private boolean pendingUnhandled;
79+
private boolean nonTerminatingUnhandledError;
7780

7881
/** The session lock, ops should be atomic */
7982
private final @NotNull AutoClosableReentrantLock sessionLock = new AutoClosableReentrantLock();
@@ -198,32 +201,41 @@ public int errorCount() {
198201
}
199202

200203
/**
201-
* Whether the session has a pending unhandled (non-terminal) exception that hasn't been finalized
202-
* yet.
204+
* Whether the session experienced an unhandled error that did not terminate the process, and so
205+
* finalizes as {@link State#Unhandled} rather than {@link State#Exited}.
203206
*/
204207
@ApiStatus.Internal
205-
public boolean isPendingUnhandled() {
206-
return pendingUnhandled;
208+
public boolean hasNonTerminatingUnhandledError() {
209+
return nonTerminatingUnhandledError;
207210
}
208211

209212
/**
210-
* Marks the session as having experienced an unhandled (non-terminal) exception without ending
211-
* it. On {@link #end()} the session will be finalized as {@link State#Unhandled} unless a crash
212-
* escalated it to {@link State#Crashed} first.
213+
* Restores the flag when rebuilding a session, i.e. from {@link #clone()} or the deserializer.
214+
*
215+
* <p>Not for use on a live session: unlike {@link #recordNonTerminatingUnhandledError()} this
216+
* neither counts the error nor advances the session's sequence, so a session mutated through this
217+
* setter would be sent as an out-of-date update.
213218
*/
214219
@ApiStatus.Internal
215-
public void setPendingUnhandled(final boolean pendingUnhandled) {
216-
this.pendingUnhandled = pendingUnhandled;
220+
public void setNonTerminatingUnhandledError(final boolean nonTerminatingUnhandledError) {
221+
this.nonTerminatingUnhandledError = nonTerminatingUnhandledError;
217222
}
218223

219-
/** Marks an active session as having experienced an unhandled non-terminal exception. */
224+
/**
225+
* Records that an active session experienced an unhandled error which did not terminate the
226+
* process, counting the error and advancing the session's sequence without ending it. On {@link
227+
* #end()} the session is finalized as {@link State#Unhandled} unless a crash escalated it to
228+
* {@link State#Crashed} first.
229+
*
230+
* @return whether the session was updated, i.e. false if it had already reached a terminal state
231+
*/
220232
@ApiStatus.Internal
221-
public boolean markPendingUnhandled() {
233+
public boolean recordNonTerminatingUnhandledError() {
222234
try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) {
223235
if (status != State.Ok) {
224236
return false;
225237
}
226-
pendingUnhandled = true;
238+
nonTerminatingUnhandledError = true;
227239
errorCount.incrementAndGet();
228240
init = null;
229241
timestamp = DateUtils.getCurrentDateTime();
@@ -255,7 +267,7 @@ public void end(final @Nullable Date timestamp) {
255267
if (status == State.Ok) {
256268
// a session that experienced an unhandled (but non-terminal) exception is finalized as
257269
// Unhandled rather than Exited.
258-
status = pendingUnhandled ? State.Unhandled : State.Exited;
270+
status = nonTerminatingUnhandledError ? State.Unhandled : State.Exited;
259271
}
260272

261273
if (timestamp != null) {
@@ -308,9 +320,9 @@ public boolean update(
308320
boolean sessionHasBeenUpdated = false;
309321
if (status != null) {
310322
this.status = status;
311-
// a real crash takes precedence over a pending unhandled (non-terminal) exception.
323+
// a crash terminates the process, so it takes precedence over a non-terminating one.
312324
if (status == State.Crashed) {
313-
pendingUnhandled = false;
325+
nonTerminatingUnhandledError = false;
314326
}
315327
sessionHasBeenUpdated = true;
316328
}
@@ -384,7 +396,7 @@ private long getSequenceTimestamp(final @NotNull Date timestamp) {
384396
environment,
385397
release,
386398
abnormalMechanism);
387-
session.setPendingUnhandled(pendingUnhandled);
399+
session.setNonTerminatingUnhandledError(nonTerminatingUnhandledError);
388400
return session;
389401
}
390402

@@ -407,7 +419,7 @@ public static final class JsonKeys {
407419
public static final String IP_ADDRESS = "ip_address";
408420
public static final String USER_AGENT = "user_agent";
409421
public static final String ABNORMAL_MECHANISM = "abnormal_mechanism";
410-
public static final String PENDING_UNHANDLED = "pending_unhandled";
422+
public static final String NON_TERMINATING_UNHANDLED_ERROR = "non_terminating_unhandled_error";
411423
}
412424

413425
@Override
@@ -438,8 +450,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
438450
if (abnormalMechanism != null) {
439451
writer.name(JsonKeys.ABNORMAL_MECHANISM).value(logger, abnormalMechanism);
440452
}
441-
if (pendingUnhandled) {
442-
writer.name(JsonKeys.PENDING_UNHANDLED).value(pendingUnhandled);
453+
if (nonTerminatingUnhandledError) {
454+
writer.name(JsonKeys.NON_TERMINATING_UNHANDLED_ERROR).value(nonTerminatingUnhandledError);
443455
}
444456
writer.name(JsonKeys.ATTRS);
445457
writer.beginObject();
@@ -497,7 +509,7 @@ public static final class Deserializer implements JsonDeserializer<Session> {
497509
String environment = null;
498510
String release = null; // @NotNull
499511
String abnormalMechanism = null;
500-
boolean pendingUnhandled = false;
512+
boolean nonTerminatingUnhandledError = false;
501513

502514
Map<String, Object> unknown = null;
503515
while (reader.peek() == JsonToken.NAME) {
@@ -541,9 +553,10 @@ public static final class Deserializer implements JsonDeserializer<Session> {
541553
case JsonKeys.ABNORMAL_MECHANISM:
542554
abnormalMechanism = reader.nextStringOrNull();
543555
break;
544-
case JsonKeys.PENDING_UNHANDLED:
545-
final Boolean pendingUnhandledValue = reader.nextBooleanOrNull();
546-
pendingUnhandled = pendingUnhandledValue != null && pendingUnhandledValue;
556+
case JsonKeys.NON_TERMINATING_UNHANDLED_ERROR:
557+
final Boolean nonTerminatingUnhandledErrorValue = reader.nextBooleanOrNull();
558+
nonTerminatingUnhandledError =
559+
nonTerminatingUnhandledErrorValue != null && nonTerminatingUnhandledErrorValue;
547560
break;
548561
case JsonKeys.ATTRS:
549562
reader.beginObject();
@@ -604,7 +617,7 @@ public static final class Deserializer implements JsonDeserializer<Session> {
604617
environment,
605618
release,
606619
abnormalMechanism);
607-
session.setPendingUnhandled(pendingUnhandled);
620+
session.setNonTerminatingUnhandledError(nonTerminatingUnhandledError);
608621
session.setUnknown(unknown);
609622
reader.endObject();
610623
return session;

‎sentry/src/main/java/io/sentry/cache/EnvelopeCache.java‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,17 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
122122
try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) {
123123
final @Nullable Session endingSession = readSessionFromEnvelope(envelope);
124124
final @Nullable Session currentSession = readSessionFromDisk(currentSessionFile);
125-
final boolean preservePendingSession =
125+
final boolean preserveCurrentSession =
126126
endingSession != null
127127
&& currentSession != null
128-
&& currentSession.isPendingUnhandled()
128+
&& currentSession.hasNonTerminatingUnhandledError()
129129
&& endingSession.getSessionId() != null
130130
&& currentSession.getSessionId() != null
131131
&& !Objects.equals(endingSession.getSessionId(), currentSession.getSessionId())
132132
&& endingSession.getStarted() != null
133133
&& currentSession.getStarted() != null
134134
&& currentSession.getStarted().after(endingSession.getStarted());
135-
if (!preservePendingSession && !currentSessionFile.delete()) {
135+
if (!preserveCurrentSession && !currentSessionFile.delete()) {
136136
options.getLogger().log(WARNING, "Current envelope doesn't exist.");
137137
}
138138
}
@@ -149,7 +149,7 @@ private boolean storeInternal(final @NotNull SentryEnvelope envelope, final @Not
149149
if (startingSession != null) {
150150
final @Nullable Session currentSession = readSessionFromDisk(currentSessionFile);
151151
if (currentSession != null && hasSameSessionId(currentSession, startingSession)) {
152-
if (!isNewerPendingOrErrorSnapshot(currentSession, startingSession)) {
152+
if (!isNewerUnhandledOrErrorSnapshot(currentSession, startingSession)) {
153153
writeSessionToDisk(currentSessionFile, startingSession);
154154
}
155155
} else {
@@ -355,9 +355,10 @@ private void writeCrashMarkerFile() {
355355
}
356356
}
357357

358-
private boolean isNewerPendingOrErrorSnapshot(
358+
private boolean isNewerUnhandledOrErrorSnapshot(
359359
final @NotNull Session currentSession, final @NotNull Session startingSession) {
360-
return (currentSession.isPendingUnhandled() && !startingSession.isPendingUnhandled())
360+
return (currentSession.hasNonTerminatingUnhandledError()
361+
&& !startingSession.hasNonTerminatingUnhandledError())
361362
|| currentSession.errorCount() > startingSession.errorCount();
362363
}
363364

‎sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,14 @@ class PreviousSessionFinalizerTest {
201201
}
202202

203203
@Test
204-
fun `if previous session has pending unhandled and no crash marker, finalizes as unhandled`() {
204+
fun `if previous session has a non-terminating unhandled error and no crash marker, finalizes as unhandled`() {
205205
val finalizer =
206206
fixture.getSut(
207207
tmpDir,
208208
session =
209-
Session(null, null, null, "io.sentry.sample@1.0").apply { setPendingUnhandled(true) },
209+
Session(null, null, null, "io.sentry.sample@1.0").apply {
210+
setNonTerminatingUnhandledError(true)
211+
},
210212
)
211213
finalizer.run()
212214

@@ -216,18 +218,20 @@ class PreviousSessionFinalizerTest {
216218
val session = fixture.sessionFromEnvelope(this)
217219
session.release == "io.sentry.sample@1.0" &&
218220
session.status == Session.State.Unhandled &&
219-
session.isPendingUnhandled
221+
session.hasNonTerminatingUnhandledError()
220222
}
221223
)
222224
}
223225

224226
@Test
225-
fun `if previous session has pending unhandled but a native crash marker exists, finalizes as crashed`() {
227+
fun `if previous session has a non-terminating unhandled error but a native crash marker exists, finalizes as crashed`() {
226228
val finalizer =
227229
fixture.getSut(
228230
tmpDir,
229231
session =
230-
Session(null, null, null, "io.sentry.sample@1.0").apply { setPendingUnhandled(true) },
232+
Session(null, null, null, "io.sentry.sample@1.0").apply {
233+
setNonTerminatingUnhandledError(true)
234+
},
231235
nativeCrashTimestamp = DateUtils.getDateTime("2023-10-01T00:00:00.000Z"),
232236
)
233237
finalizer.run()

0 commit comments

Comments
 (0)