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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start a session after init if AutoSessionTracking is enabled #2356

Merged
merged 17 commits into from Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
### Features

- Update Spring Boot Jakarta to Spring Boot 3.0.0-RC2 ([#2347](https://github.com/getsentry/sentry-java/pull/2347))
- Start a session after init if AutoSessionTracking is enabled ([#2356](https://github.com/getsentry/sentry-java/pull/2356))

## 6.7.0

Expand Down
Expand Up @@ -5,6 +5,7 @@
import io.sentry.Breadcrumb;
import io.sentry.IHub;
import io.sentry.SentryLevel;
import io.sentry.Session;
import io.sentry.transport.CurrentDateProvider;
import io.sentry.transport.ICurrentDateProvider;
import java.util.Timer;
Expand Down Expand Up @@ -74,15 +75,25 @@ private void startSession() {
cancelTask();

final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
final long lastUpdatedSession = this.lastUpdatedSession.get();

if (lastUpdatedSession == 0L
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
addSessionBreadcrumb("start");
hub.startSession();
runningSession.set(true);
}
this.lastUpdatedSession.set(currentTimeMillis);
hub.withScope(
scope -> {
long lastUpdatedSession = this.lastUpdatedSession.get();
if (lastUpdatedSession == 0L) {
@Nullable Session currentSession = scope.getSession();
if (currentSession != null && currentSession.getStarted() != null) {
lastUpdatedSession = currentSession.getStarted().getTime();
markushi marked this conversation as resolved.
Show resolved Hide resolved
}
}

if (lastUpdatedSession == 0L
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
addSessionBreadcrumb("start");
hub.startSession();
runningSession.set(true);
}
this.lastUpdatedSession.set(currentTimeMillis);
});
}
}

Expand Down
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import android.os.SystemClock;
import io.sentry.DateUtils;
import io.sentry.IHub;
import io.sentry.ILogger;
import io.sentry.Integration;
import io.sentry.OptionsContainer;
Expand Down Expand Up @@ -109,6 +110,11 @@ public static synchronized void init(
resetEnvelopeCacheIfNeeded(options);
},
true);

final @NotNull IHub hub = Sentry.getCurrentHub();
if (hub.getOptions().isEnableAutoSessionTracking()) {
hub.startSession();
markushi marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (IllegalAccessException e) {
logger.log(SentryLevel.FATAL, "Fatal error during SentryAndroid.init(...)", e);

Expand Down
Expand Up @@ -2,17 +2,24 @@ package io.sentry.android.core

import androidx.lifecycle.LifecycleOwner
import io.sentry.Breadcrumb
import io.sentry.DateUtils
import io.sentry.IHub
import io.sentry.Scope
import io.sentry.ScopeCallback
import io.sentry.SentryLevel
import io.sentry.Session
import io.sentry.Session.State
import io.sentry.transport.ICurrentDateProvider
import org.awaitility.kotlin.await
import org.mockito.ArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.check
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
Expand All @@ -25,8 +32,26 @@ class LifecycleWatcherTest {
val hub = mock<IHub>()
val dateProvider = mock<ICurrentDateProvider>()

fun getSUT(sessionIntervalMillis: Long = 0L, enableAutoSessionTracking: Boolean = true, enableAppLifecycleBreadcrumbs: Boolean = true): LifecycleWatcher {
return LifecycleWatcher(hub, sessionIntervalMillis, enableAutoSessionTracking, enableAppLifecycleBreadcrumbs, dateProvider)
fun getSUT(
sessionIntervalMillis: Long = 0L,
enableAutoSessionTracking: Boolean = true,
enableAppLifecycleBreadcrumbs: Boolean = true,
session: Session? = null
): LifecycleWatcher {
val argumentCaptor: ArgumentCaptor<ScopeCallback> = ArgumentCaptor.forClass(ScopeCallback::class.java)
val scope = mock<Scope>()
whenever(scope.session).thenReturn(session)
whenever(hub.withScope(argumentCaptor.capture())).thenAnswer {
argumentCaptor.value.run(scope)
}

return LifecycleWatcher(
hub,
sessionIntervalMillis,
enableAutoSessionTracking,
enableAppLifecycleBreadcrumbs,
dateProvider
)
}
}

Expand Down Expand Up @@ -193,4 +218,54 @@ class LifecycleWatcherTest {
val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false)
assertNull(watcher.timer)
}

@Test
fun `if the hub has already a fresh session running, don't start new one`() {
val watcher = fixture.getSUT(
enableAppLifecycleBreadcrumbs = false,
session = Session(
State.Ok,
DateUtils.getCurrentDateTime(),
DateUtils.getCurrentDateTime(),
0,
"abc",
UUID.fromString("3c1ffc32-f68f-4af2-a1ee-dd72f4d62d17"),
true,
0,
10.0,
null,
null,
null,
"release"
)
)

watcher.onStart(fixture.ownerMock)
verify(fixture.hub, never()).startSession()
}

@Test
fun `if the hub has a long running session, start new one`() {
val watcher = fixture.getSUT(
enableAppLifecycleBreadcrumbs = false,
session = Session(
State.Ok,
DateUtils.getDateTime(-1),
DateUtils.getDateTime(-1),
0,
"abc",
UUID.fromString("3c1ffc32-f68f-4af2-a1ee-dd72f4d62d17"),
true,
0,
10.0,
null,
null,
null,
"release"
)
)

watcher.onStart(fixture.ownerMock)
verify(fixture.hub).startSession()
}
}
Expand Up @@ -25,6 +25,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
Expand Down Expand Up @@ -163,6 +164,26 @@ class SentryAndroidTest {
assertTrue { refOptions!!.envelopeDiskCache is CustomEnvelopCache }
}

@Test
fun `init starts a session if auto session tracking is enabled`() {
fixture.initSut { options ->
options.isEnableAutoSessionTracking = true
}
Sentry.getCurrentHub().withScope { scope ->
assertNotNull(scope.session)
}
}

@Test
fun `init does not start a session by if auto session tracking is disabled`() {
fixture.initSut { options ->
options.isEnableAutoSessionTracking = false
}
Sentry.getCurrentHub().withScope { scope ->
assertNull(scope.session)
}
}

private class CustomEnvelopCache : IEnvelopeCache {
override fun iterator(): MutableIterator<SentryEnvelope> = TODO()
override fun store(envelope: SentryEnvelope, hint: Hint) = Unit
Expand Down
1 change: 1 addition & 0 deletions sentry/api/sentry.api
Expand Up @@ -963,6 +963,7 @@ public final class io/sentry/Scope {
public fun getContexts ()Lio/sentry/protocol/Contexts;
public fun getLevel ()Lio/sentry/SentryLevel;
public fun getRequest ()Lio/sentry/protocol/Request;
public fun getSession ()Lio/sentry/Session;
public fun getSpan ()Lio/sentry/ISpan;
public fun getTags ()Ljava/util/Map;
public fun getTransaction ()Lio/sentry/ITransaction;
Expand Down
5 changes: 5 additions & 0 deletions sentry/src/main/java/io/sentry/Scope.java
Expand Up @@ -762,6 +762,11 @@ public void withTransaction(final @NotNull IWithTransaction callback) {
}
}

@ApiStatus.Internal
public @Nullable Session getSession() {
return session;
}

/** the IWithTransaction callback */
@ApiStatus.Internal
public interface IWithTransaction {
Expand Down