Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Fallback to distinct-id as user.id logging attribute when user is not set ([#4847](https://github.com/getsentry/sentry-java/pull/4847))
- Report Timber.tag() as `timber.tag` log attribute ([#4845](https://github.com/getsentry/sentry-java/pull/4845))
- Session Replay: Add screenshot strategy serialization to RRWeb events ([#4851](https://github.com/getsentry/sentry-java/pull/4851))
- Android: Flush log when app enters background ([#4873](https://github.com/getsentry/sentry-java/pull/4873))

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions
this.options.isEnableAppLifecycleBreadcrumbs());

if (this.options.isEnableAutoSessionTracking()
|| this.options.isEnableAppLifecycleBreadcrumbs()) {
|| this.options.isEnableAppLifecycleBreadcrumbs()
|| this.options.getLogs().isEnabled()) {
try (final ISentryLifecycleToken ignored = lock.acquire()) {
if (watcher != null) {
return;
Expand All @@ -56,7 +57,8 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions
scopes,
this.options.getSessionTrackingIntervalMillis(),
this.options.isEnableAutoSessionTracking(),
this.options.isEnableAppLifecycleBreadcrumbs());
this.options.isEnableAppLifecycleBreadcrumbs(),
this.options.getLogs().isEnabled());

AppState.getInstance().addAppStateListener(watcher);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.sentry.ISentryLifecycleToken;
import io.sentry.SentryLevel;
import io.sentry.Session;
import io.sentry.logger.LoggerBatchProcessor;
import io.sentry.transport.CurrentDateProvider;
import io.sentry.transport.ICurrentDateProvider;
import io.sentry.util.AutoClosableReentrantLock;
Expand All @@ -29,18 +30,22 @@ final class LifecycleWatcher implements AppState.AppStateListener {
private final boolean enableSessionTracking;
private final boolean enableAppLifecycleBreadcrumbs;

private final boolean enableLogFlushing;

private final @NotNull ICurrentDateProvider currentDateProvider;

LifecycleWatcher(
final @NotNull IScopes scopes,
final long sessionIntervalMillis,
final boolean enableSessionTracking,
final boolean enableAppLifecycleBreadcrumbs) {
final boolean enableAppLifecycleBreadcrumbs,
final boolean enableLogFlushing) {
this(
scopes,
sessionIntervalMillis,
enableSessionTracking,
enableAppLifecycleBreadcrumbs,
enableLogFlushing,
CurrentDateProvider.getInstance());
}

Expand All @@ -49,10 +54,12 @@ final class LifecycleWatcher implements AppState.AppStateListener {
final long sessionIntervalMillis,
final boolean enableSessionTracking,
final boolean enableAppLifecycleBreadcrumbs,
final boolean enableLogFlushing,
final @NotNull ICurrentDateProvider currentDateProvider) {
this.sessionIntervalMillis = sessionIntervalMillis;
this.enableSessionTracking = enableSessionTracking;
this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs;
this.enableLogFlushing = enableLogFlushing;
this.scopes = scopes;
this.currentDateProvider = currentDateProvider;
}
Expand Down Expand Up @@ -101,6 +108,29 @@ public void onBackground() {
scheduleEndSession();

addAppBreadcrumb("background");

if (enableLogFlushing) {
try {
scopes
.getOptions()
.getExecutorService()
.submit(
new Runnable() {
@Override
public void run() {
scopes
.getGlobalScope()
.getClient()
.flushLogs(LoggerBatchProcessor.FLUSH_AFTER_MS);
}
});
} catch (Throwable t) {
scopes
.getOptions()
.getLogger()
.log(SentryLevel.ERROR, t, "Failed to submit log flush runnable");
}
}
}

private void scheduleEndSession() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ class AppLifecycleIntegrationTest {
}

@Test
fun `When SessionTracking and AppLifecycle breadcrumbs are disabled, lifecycle watcher should not be started`() {
fun `When SessionTracking and AppLifecycle breadcrumbs and Logs are disabled, lifecycle watcher should not be started`() {
val sut = fixture.getSut()
fixture.options.apply {
isEnableAppLifecycleBreadcrumbs = false
isEnableAutoSessionTracking = false
logs.isEnabled = false
}

sut.register(fixture.scopes, fixture.options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import io.sentry.DateUtils
import io.sentry.IContinuousProfiler
import io.sentry.IScope
import io.sentry.IScopes
import io.sentry.ISentryClient
import io.sentry.ReplayController
import io.sentry.ScopeCallback
import io.sentry.SentryLevel
import io.sentry.SentryOptions
import io.sentry.Session
import io.sentry.Session.State
import io.sentry.test.ImmediateExecutorService
import io.sentry.transport.ICurrentDateProvider
import kotlin.test.BeforeTest
import kotlin.test.Test
Expand All @@ -36,10 +38,13 @@ class LifecycleWatcherTest {
val replayController = mock<ReplayController>()
val continuousProfiler = mock<IContinuousProfiler>()

val client = mock<ISentryClient>()

fun getSUT(
sessionIntervalMillis: Long = 0L,
enableAutoSessionTracking: Boolean = true,
enableAppLifecycleBreadcrumbs: Boolean = true,
enableLogFlushing: Boolean = true,
session: Session? = null,
): LifecycleWatcher {
val argumentCaptor: ArgumentCaptor<ScopeCallback> =
Expand All @@ -49,15 +54,20 @@ class LifecycleWatcherTest {
whenever(scopes.configureScope(argumentCaptor.capture())).thenAnswer {
argumentCaptor.value.run(scope)
}
whenever(scope.client).thenReturn(client)

options.setReplayController(replayController)
options.setContinuousProfiler(continuousProfiler)
options.executorService = ImmediateExecutorService()
whenever(scopes.options).thenReturn(options)
whenever(scopes.globalScope).thenReturn(scope)

return LifecycleWatcher(
scopes,
sessionIntervalMillis,
enableAutoSessionTracking,
enableAppLifecycleBreadcrumbs,
enableLogFlushing,
dateProvider,
)
}
Expand Down Expand Up @@ -295,4 +305,27 @@ class LifecycleWatcherTest {
watcher.onBackground()
verify(fixture.replayController, timeout(10000)).stop()
}

@Test
fun `flush logs when going in background`() {
val watcher = fixture.getSUT(enableLogFlushing = true)

watcher.onForeground()
watcher.onBackground()

watcher.onForeground()
watcher.onBackground()

verify(fixture.client, times(2)).flushLogs(any())
}

@Test
fun `do not flush logs when going in background when logging is disabled`() {
val watcher = fixture.getSUT(enableLogFlushing = false)

watcher.onForeground()
watcher.onBackground()

verify(fixture.client, never()).flushLogs(any())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ class SessionTrackingIntegrationTest {
TODO("Not yet implemented")
}

override fun flushLogs(timeoutMillis: Long) {
TODO("Not yet implemented")
}

override fun captureFeedback(feedback: Feedback, hint: Hint?, scope: IScope): SentryId {
TODO("Not yet implemented")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
android:exported="false" />

<!-- NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard-->
<meta-data android:name="io.sentry.dsn" android:value="https://1053864c67cc410aa1ffc9701bd6f93d@o447951.ingest.us.sentry.io/5428559" />
<meta-data android:name="io.sentry.dsn" android:value="https://8a26495179336af214ba94bafed37efc@o447951.ingest.us.sentry.io/4509161790832641" />

<!-- how to enable Sentry's debug mode-->
<meta-data android:name="io.sentry.debug" android:value="${sentryDebug}" />
Expand Down Expand Up @@ -133,11 +133,11 @@
<!-- <meta-data android:name="io.sentry.traces.profiling.sample-rate" android:value="1.0" />-->

<!-- Enable profiling, adjust in production env -->
<meta-data android:name="io.sentry.traces.profiling.session-sample-rate" android:value="1.0" />
<meta-data android:name="io.sentry.traces.profiling.session-sample-rate" android:value="0.0" />
<!-- Set profiling lifecycle, can be `manual` or `trace` -->
<meta-data android:name="io.sentry.traces.profiling.lifecycle" android:value="manual" />
<!-- Enable profiling on app start -->
<meta-data android:name="io.sentry.traces.profiling.start-on-app-start" android:value="true" />
<meta-data android:name="io.sentry.traces.profiling.start-on-app-start" android:value="false" />

<!-- how to disable the Activity auto instrumentation for tracing-->
<!-- <meta-data android:name="io.sentry.traces.activity.enable" android:value="false" />-->
Expand Down Expand Up @@ -184,10 +184,8 @@
<!-- how to disable sentry -->
<!-- <meta-data android:name="io.sentry.enabled" android:value="false" /> -->

<meta-data android:name="io.sentry.performance-v2.enable" android:value="true" />

<meta-data android:name="io.sentry.session-replay.screenshot-strategy" android:value="canvas" />
<meta-data android:name="io.sentry.session-replay.session-sample-rate" android:value="1" />
<meta-data android:name="io.sentry.session-replay.session-sample-rate" android:value="0.0" />
<meta-data android:name="io.sentry.session-replay.mask-all-text" android:value="true" />
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,26 @@ public void run() {
Sentry.logger().log(SentryLogLevel.INFO, "MainActivity created");
}

@Override
protected void onStart() {
super.onStart();
final ISpan span = Sentry.getSpan();
if (span != null) {
span.startChild("data.load", "MainActivity: 001");
new Thread(
() -> {
try {
Thread.sleep(25000L);
} catch (Exception e) {
// ignored
}
// span.finish();
},
"data.load")
.start();
}
}

private void stackOverflow() {
stackOverflow();
}
Expand Down
2 changes: 2 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,7 @@ public abstract interface class io/sentry/ISentryClient {
public abstract fun close ()V
public abstract fun close (Z)V
public abstract fun flush (J)V
public abstract fun flushLogs (J)V
public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
public abstract fun isEnabled ()Z
public fun isHealthy ()Z
Expand Down Expand Up @@ -2824,6 +2825,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient {
public fun close ()V
public fun close (Z)V
public fun flush (J)V
public fun flushLogs (J)V
public fun getRateLimiter ()Lio/sentry/transport/RateLimiter;
public fun isEnabled ()Z
public fun isHealthy ()Z
Expand Down
2 changes: 2 additions & 0 deletions sentry/src/main/java/io/sentry/ISentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public interface ISentryClient {
*/
void flush(long timeoutMillis);

void flushLogs(long timeoutMillis);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the biggest fan of adding a new API here, an alternative would be to move this directly into ILoggerApi


/**
* Captures the event.
*
Expand Down
3 changes: 3 additions & 0 deletions sentry/src/main/java/io/sentry/NoOpSentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public void close() {}
@Override
public void flush(long timeoutMillis) {}

@Override
public void flushLogs(long timeoutMillis) {}

@Override
public @NotNull SentryId captureFeedback(
@NotNull Feedback feedback, @Nullable Hint hint, @NotNull IScope scope) {
Expand Down
7 changes: 6 additions & 1 deletion sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1542,10 +1542,15 @@ public void close(final boolean isRestarting) {

@Override
public void flush(final long timeoutMillis) {
loggerBatchProcessor.flush(timeoutMillis);
flushLogs(timeoutMillis);
transport.flush(timeoutMillis);
}

@Override
public void flushLogs(final long timeoutMillis) {
loggerBatchProcessor.flush(timeoutMillis);
}

@Override
public @Nullable RateLimiter getRateLimiter() {
return transport.getRateLimiter();
Expand Down
Loading