Skip to content

fix(logcat): Add re-entrancy guard to SentryLogcatAdapter#5734

Open
sentry[bot] wants to merge 5 commits into
mainfrom
seer/fix/logcat-recursion-stackoverflow
Open

fix(logcat): Add re-entrancy guard to SentryLogcatAdapter#5734
sentry[bot] wants to merge 5 commits into
mainfrom
seer/fix/logcat-recursion-stackoverflow

Conversation

@sentry

@sentry sentry Bot commented Jul 7, 2026

Copy link
Copy Markdown

📜 Description

Fixes a StackOverflowError (SDK-CRASHES-JAVA-3T3H) caused by infinite recursion in SentryLogcatAdapter when the Sentry Android Gradle Plugin's logcat instrumentation is enabled.

💡 Motivation and Context

The Sentry Android Gradle Plugin rewrites android.util.Log.* calls in the app's own code to SentryLogcatAdapter.*. SDK classes under io.sentry.* are explicitly excluded from this rewrite (see Logcat.isInstrumentable / isSentryClass in the plugin), so AndroidLogger and the adapter itself are not transformed.

The recursion instead happens when code that runs while a Logcat breadcrumb or log is being captured itself calls a rewritten Log.* method. The most common paths are an app beforeBreadcrumb or beforeSendLog callback (or anything they invoke) that logs:

  1. SentryLogcatAdapter.w()addAsBreadcrumb() / addAsLog()
  2. → the app's beforeBreadcrumb / beforeSendLog callback runs
  3. → the callback calls Log.w(...), which the plugin rewrote to SentryLogcatAdapter.w(...)
  4. → back to step 1 → infinite recursion → StackOverflowError

Solution
A single ThreadLocal<Boolean> re-entrancy guard (isCapturing) wraps both the breadcrumb path (addAsBreadcrumb()) and the logs path (addAsLog()). If either is re-entered on the same thread while a capture is already in progress, the nested call returns early, breaking the cycle. The real Log.* call is left outside the guard so the app's actual logcat output is preserved.

This fix lives in the SDK (not the plugin) because the recursion is a runtime cycle through arbitrary app code reachable during breadcrumb/log processing — something the plugin cannot detect statically.

💚 How did you test it?

Added two regression tests (using Google Truth) that install a beforeBreadcrumb / beforeSendLog callback which re-enters SentryLogcatAdapter. Each was verified to fail with a StackOverflowError without its guard and pass with it, recording exactly one breadcrumb / log.

Fixes SDK-CRASHES-JAVA-3T3H

JAVA-637

Triggered by @runningcode

@sentry

sentry Bot commented Jul 7, 2026

Copy link
Copy Markdown
Author

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.47.0 (1) release

⚙️ sentry-android Build Distribution Settings

Comment thread sentry-android-core/src/main/java/io/sentry/android/core/SentryLogcatAdapter.java Outdated
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 319.54 ms 372.94 ms 53.40 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d364ace 382.77 ms 443.21 ms 60.44 ms
4c04bb8 333.16 ms 408.16 ms 75.00 ms
bb0ff41 344.70 ms 413.82 ms 69.12 ms
91bb874 314.47 ms 440.00 ms 125.53 ms
bdbe1f4 380.66 ms 464.44 ms 83.78 ms
8687935 294.00 ms 304.02 ms 10.02 ms
eb95ded 317.51 ms 369.08 ms 51.57 ms
b936425 302.69 ms 372.86 ms 70.17 ms
d15471f 342.08 ms 415.44 ms 73.35 ms
5b66efd 308.67 ms 363.85 ms 55.18 ms

App size

Revision Plain With Sentry Diff
d364ace 1.58 MiB 2.11 MiB 539.75 KiB
4c04bb8 0 B 0 B 0 B
bb0ff41 0 B 0 B 0 B
91bb874 1.58 MiB 2.13 MiB 559.07 KiB
bdbe1f4 1.58 MiB 2.11 MiB 538.88 KiB
8687935 1.58 MiB 2.19 MiB 619.17 KiB
eb95ded 0 B 0 B 0 B
b936425 0 B 0 B 0 B
d15471f 1.58 MiB 2.13 MiB 559.54 KiB
5b66efd 1.58 MiB 2.13 MiB 559.07 KiB

Previous results on branch: seer/fix/logcat-recursion-stackoverflow

Startup times

Revision Plain With Sentry Diff
2b9b854 295.38 ms 351.88 ms 56.50 ms
46b96df 314.37 ms 361.13 ms 46.76 ms

App size

Revision Plain With Sentry Diff
2b9b854 0 B 0 B 0 B
46b96df 0 B 0 B 0 B

runningcode and others added 3 commits July 7, 2026 14:23
Add a regression test reproducing SDK-CRASHES-JAVA-3T3H: a beforeBreadcrumb
callback that logs re-enters SentryLogcatAdapter while a Logcat breadcrumb is
being added, which recurses to a StackOverflowError without the guard.

Also correct the guard's Javadoc. The recursion is not caused by the plugin
transforming AndroidLogger inside the SDK (io.sentry.* classes are excluded
from logcat instrumentation); it is caused by app code reachable during
breadcrumb processing calling a rewritten Log.* method.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The guard only covered addAsBreadcrumb(). The public methods also call
addAsLog() sequentially, which could recurse the same way: a beforeSendLog
callback that logs re-enters SentryLogcatAdapter while a Logcat log is being
sent, leading to a StackOverflowError.

Share a single thread-local guard across both addAsBreadcrumb() and
addAsLog() so any re-entrant capture on the same thread is a no-op, while the
real Log.* call is left untouched. Add a regression test for the logs path
and use Google Truth for the new assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 7, 2026

Copy link
Copy Markdown

JAVA-637

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants