Skip to content

Commit 716b574

Browse files
committed
Debounce low memory breadcrumbs
1 parent 48b332f commit 716b574

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import io.sentry.Integration;
1313
import io.sentry.SentryLevel;
1414
import io.sentry.SentryOptions;
15+
import io.sentry.android.core.internal.util.AndroidCurrentDateProvider;
16+
import io.sentry.android.core.internal.util.Debouncer;
1517
import io.sentry.android.core.internal.util.DeviceOrientations;
1618
import io.sentry.protocol.Device;
1719
import io.sentry.util.Objects;
@@ -24,10 +26,17 @@
2426
public final class AppComponentsBreadcrumbsIntegration
2527
implements Integration, Closeable, ComponentCallbacks2 {
2628

29+
private static final long DEBOUNCE_WAIT_TIME_MS = 60 * 1000;
30+
// pre-allocate hint to avoid creating it every time for the low memory case
31+
private static final @NotNull Hint EMPTY_HINT = new Hint();
32+
2733
private final @NotNull Context context;
2834
private @Nullable IScopes scopes;
2935
private @Nullable SentryAndroidOptions options;
3036

37+
private final @NotNull Debouncer trimMemoryDebouncer =
38+
new Debouncer(AndroidCurrentDateProvider.getInstance(), DEBOUNCE_WAIT_TIME_MS, 0);
39+
3140
public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) {
3241
this.context =
3342
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
@@ -109,6 +118,11 @@ public void onTrimMemory(final int level) {
109118
return;
110119
}
111120

121+
if (trimMemoryDebouncer.checkForDebounce()) {
122+
// if we received trim_memory within 1 minute time, ignore this call
123+
return;
124+
}
125+
112126
final long now = System.currentTimeMillis();
113127
executeInBackground(() -> captureLowMemoryBreadcrumb(now, level));
114128
}
@@ -122,7 +136,7 @@ private void captureLowMemoryBreadcrumb(final long timeMs, final int level) {
122136
breadcrumb.setData("action", "LOW_MEMORY");
123137
breadcrumb.setData("level", level);
124138
breadcrumb.setLevel(SentryLevel.WARNING);
125-
scopes.addBreadcrumb(breadcrumb);
139+
scopes.addBreadcrumb(breadcrumb, EMPTY_HINT);
126140
}
127141
}
128142

sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.mockito.kotlin.check
1515
import org.mockito.kotlin.mock
1616
import org.mockito.kotlin.never
1717
import org.mockito.kotlin.verify
18+
import org.mockito.kotlin.verifyNoMoreInteractions
1819
import org.mockito.kotlin.whenever
1920
import java.lang.NullPointerException
2021
import kotlin.test.Test
@@ -109,7 +110,8 @@ class AppComponentsBreadcrumbsIntegrationTest {
109110
assertEquals("device.event", it.category)
110111
assertEquals("system", it.type)
111112
assertEquals(SentryLevel.WARNING, it.level)
112-
}
113+
},
114+
anyOrNull()
113115
)
114116
}
115117

@@ -144,4 +146,26 @@ class AppComponentsBreadcrumbsIntegrationTest {
144146
anyOrNull()
145147
)
146148
}
149+
150+
@Test
151+
fun `low memory changes are debounced`() {
152+
val sut = fixture.getSut()
153+
154+
val scopes = mock<IScopes>()
155+
val options = SentryAndroidOptions().apply {
156+
executorService = ImmediateExecutorService()
157+
}
158+
sut.register(scopes, options)
159+
sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND)
160+
sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL)
161+
162+
// should only add the first crumb
163+
verify(scopes).addBreadcrumb(
164+
check<Breadcrumb> {
165+
assertEquals(it.data["level"], 40)
166+
},
167+
anyOrNull()
168+
)
169+
verifyNoMoreInteractions(scopes)
170+
}
147171
}

0 commit comments

Comments
 (0)